How is this particular Rust code implemented? - rust

I'm reading the documentation for the Amethyst game engine, and I've encountered this interesting piece of code:
use amethyst::ecs::World;
struct MyResource {
pub game_score: i32,
}
fn main() {
let mut world = World::empty();
let my = MyResource {
game_score: 0,
};
world.insert(my);
}
And:
// try_fetch returns a Option<Fetch<MyResource>>
let fetched = world.try_fetch::<MyResource>();
if let Some(fetched_resource) = fetched {
//dereference Fetch<MyResource> to access data
assert_eq!(*fetched_resource, MyResource{ game_score: 0, });
} else {
println!("No MyResource present in `World`");
}
And I don't see how it can work, really.
Apparently, there is some kind of collection inside World that can hold members of a completely arbitrary data type -- how can that be possible? The MyResource struct doesn't implement any particular trait, so we can't say something like Box<dyn Trait>.
And then we can recover that item based on its type. Again, I can't see how this can work. How can we iterate through a collection and check every item's type? Is there even a dynamic type checker in Rust? Again, the struct does not implement any particular trait, so how can it possibly interface with it at all?

The signature of insert:
pub fn insert<R>(&mut self, r: R) where
R: Resource,
There is a blanket implementation for Resource
impl<T> Resource for T where
T: Any + Send + Sync,
There is also a blanket implementation for Any (Any trait implementors):
impl<T> Any for T where
T: 'static + ?Sized,
The Any docs are probably also relevant.
Conclusion: your MyResource does implement a relevant trait, namely the Resource trait.

Related

Associated function `display` is never used

There is a basic struct implementation that gives me this warning. I want to create methods on a struct. As far as I understand (please correct me if I am wrong), the difference between methods and associative functions is the &self parameter. But for some reason, rust is taking those as associative functions:
struct User {
name: String,
date: u64,
}
impl User {
fn display(&self) {
println!("{}", self.name);
}
}
Here is the warning which I am getting:
associated function `display` is never usedrustc(dead_code)
According to this resource (https://doc.rust-lang.org/book/ch05-03-method-syntax.html), I think this is the correct way to write methods.
Methods are associated functions, they are associated to the Type. You can even say var.method() is just syntax sugar for Type::method(&var), as they do the exact same thing.
Also, while there's nothing wrong with your implementation, for common functions like display, it is more idiomatic to implement the Display trait.
struct User {
name: String,
date: u64,
}
use std::fmt::{Display, Formatter};
impl Display for User {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", self.name)
}
}
Once this trait is implemented, you can print User like this, also you get to_string() for free
let user = User { /* init */ };
println!("{}", user);
let user_s: String = user.to_string();
If you want to know more about traits, checkout the trait section in the rust book

How to specify associated type <Item=...> when calling associated function of a trait?

I have this rust code playground:
use itertools::{repeat_n,RepeatN};
pub trait MyIterator: Iterator {
fn fill1(elem: Self::Item, n1: usize) -> RepeatN<Self::Item>
where
Self::Item: Clone,
{
repeat_n(elem, n1)
}
}
My Problem is that I can't call this method because rustc can't infer the type.
// error[E0284]: type annotations needed
// let r: RepeatN<char> = MyIterator::fill1('a', 5);
// ^^^^^^^^^^^^^^^^^ cannot infer type
// note: cannot satisfy `<_ as Iterator>::Item == _`
let r: RepeatN<char> = MyIterator::fill1('a', 5);
I tried this but it doesn't compile:
// error[E0229]: associated type bindings are not allowed here
let r: RepeatN<char> = MyIterator::<Item=char>::fill1('a', 5);
How can I specify the type of Item in this call? Or is a function outside of the trait (like itertools::repeat_n) the best way here?
Well, you haven't implemented the trait for any types, so no fill1 function actually exists.
If you implement MyIterator for some type that implements Iterator<Item = char>, for example std::iter::Empty<char>, then you'll be able to call fill1 through that type.
use std::iter::Empty;
impl MyIterator for Empty<char> {}
fn main() {
let r: RepeatN<char> = Empty::fill1('a', 5);
}
This is, however, pointless. You will note that Empty plays no role in the actual function definition - it could have been any iterator. Furthermore there is no sensible generalization of this behavior to other Self types. This is why itertools::repeat_n is not a member of the Itertools trait: it is nonsense as a trait function because it is not a generalization of behavior shared by multiple iterator types.
In Rust, unlike some other languages, not everything has to be a member of a class. If you merely want to put fill1 and related things in a common namespace, simply use a module, which is the most basic unit of code organization.
mod my_iterator {
use itertools::{repeat_n, RepeatN};
fn fill1<T>(elem: T, n1: usize) -> RepeatN<T>
where
T: Clone,
{
repeat_n(elem, n1)
}
}

Store a collection of heterogeneous types with generic type parameters in Rust

I'm trying to implement a basic ECS in Rust. I want a data structure storing, for each component, a storage of that particular component. Because some components are common while others are rare, I want different types of storage policies such as VecStorage<T> and HashMapStorage<T>.
As components are unknown to the game engine's ECS, I came up with:
trait AnyStorage: Debug {
fn new() -> Self
where
Self: Sized;
}
#[derive(Default, Debug)]
struct StorageMgr {
storages: HashMap<TypeId, Box<AnyStorage>>,
}
with VecStorage and HashMapStorage<T> implementing the AnyStorage trait. Since AnyStorage doesn't know T, I added one more trait implemented by both concrete storages: ComponentStorage<T>.
While I was able to register new components (i.e. add a new Box<AnyStorage> in StorageMgr's storages), I didn't find a way to insert components.
Here is the erroneous code:
pub fn add_component_to_storage<C: Component>(&mut self, component: C) {
let storage = self.storages.get_mut(&TypeId::of::<C>()).unwrap();
// storage is of type: &mut Box<AnyStorage + 'static>
println!("{:?}", storage); // Prints "VecStorage([])"
storage.insert(component); // This doesn't work
// This neither:
// let any_stor: &mut Any = storage;
// let storage = any_stor.downcast_ref::<ComponentStorage<C>>();
}
I know that my problem comes from the fact that storage's type is &mut Box<AnyStorage>; can I obtain the concrete VecStorage from it?
The whole point of doing all this is that I want components to be contiguous in memory and to have different storage for each component type. I can not resolve myself to use Box<Component>, or I don't see how.
I reduced my problem to a minimal code on Rust Playground.
I wasn't sure if something like this was possible but I've finally figured it out. There are a couple things to note as to why your posted example was failing.
Trait AnyStorage in your example did not implement ComponentStorage<T>, therefore because you were storing your "storage"s in a HashMap<TypeId, Box<AnyStorage>>, Rust could not guarantee that every stored type implemented ComponentStorage<T>::insert() because it only knew that they were AnyStorages.
If you did combine the two traits into one simply called Storage<T> and stored them in a HashMap<TypeId, Box<Storage<T>>, every version of Storage would have to store the same type because of the single T. Rust doesn't have a way to dynamically type the values of a map based on the TypeId of the key, as a solution like this would require. Also, you can't replace T with Any because Any isn't Sized, which Vec and all other storage types require. I'm guessing you knew all of this which is why you used two different traits in your original example.
The solution I ended up using stored the Storage<T>s as Anys in a HashMap<TypeId, Box<Any>>, and then I downcasted the Anys into Storage<T>s inside the implementation functions for StorageMgr. I've put a short example below, and a full version is on Rust Playground here
.
trait Component: Debug + Sized + Any {
type Storage: Storage<Self>;
}
trait Storage<T: Debug>: Debug + Any {
fn new() -> Self
where
Self: Sized;
fn insert(&mut self, value: T);
}
struct StorageMgr {
storages: HashMap<TypeId, Box<Any>>,
}
impl StorageMgr {
pub fn new() -> Self {
Self {
storages: HashMap::new(),
}
}
pub fn get_storage_mut<C: Component>(&mut self) -> &mut <C as Component>::Storage {
let type_id = TypeId::of::<C>();
// Add a storage if it doesn't exist yet
if !self.storages.contains_key(&type_id) {
let new_storage = <C as Component>::Storage::new();
self.storages.insert(type_id, Box::new(new_storage));
}
// Get the storage for this type
match self.storages.get_mut(&type_id) {
Some(probably_storage) => {
// Turn the Any into the storage for that type
match probably_storage.downcast_mut::<<C as Component>::Storage>() {
Some(storage) => storage,
None => unreachable!(), // <- you may want to do something less explosive here
}
}
None => unreachable!(),
}
}
}

Use Trait as Vec Type

I'm new to Rust and have seen some examples of people using Box to allow pushing many types that implement a certain Trait onto a Vec. When using a Trait with Generics, I have run into an issue.
error[E0038]: the trait `collision::collision_detection::Collidable` cannot be made into an object
--> src/collision/collision_detection.rs:19:5
|
19 | collidables: Vec<Box<Collidable<P, M>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `collision::collision_detection::Collidable` cannot be made into an object
|
= note: method `get_ncollide_shape` has generic type parameters
error: aborting due to previous error
error: Could not compile `game_proto`.
To learn more, run the command again with --verbose.
Here is my code
extern crate ncollide;
extern crate nalgebra as na;
use self::ncollide::shape::Shape;
use self::ncollide::math::Point;
use self::ncollide::math::Isometry;
use self::na::Isometry2;
pub trait Collidable<P: Point, M> {
fn get_ncollide_shape<T: Shape<P, M>>(&self) -> Box<T>;
fn get_isometry(&self) -> Isometry2<f64>;
}
pub struct CollisionRegistry<P, M>
where
P: Point,
M: Isometry<P>,
{
collidables: Vec<Box<Collidable<P, M>>>,
}
impl<P: Point, M: Isometry<P>> CollisionRegistry<P, M> {
pub fn new() -> Self {
let objs: Vec<Box<Collidable<P, M>>> = Vec::new();
CollisionRegistry { collidables: objs }
}
pub fn register<D>(&mut self, obj: Box<D>)
where
D: Collidable<P, M>,
{
self.collidables.push(obj);
}
}
I'm trying to use collidables as a list of heterogenous game objects that will give me ncollide compatible Shapes back to feed into the collision detection engine.
EDIT:
To clear up some confusion. I'm not trying to construct and return an instance of a Trait. I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it.
Rust is a compiled language, so when it compiles your code, it needs to know all of the information it might need to generate machine code.
When you say
trait MyTrait {
fn do_thing() -> Box<u32>;
}
struct Foo {
field: Box<MyTrait>
}
you are telling Rust that Foo will contain a box containing anything implementing MyTrait. By boxing the type, the compiler will erase any additional data about the data type that isn't covered by the trait. These trait objects are implemented as a set of data fields and a table of functions (called a vtable) that contains the functions exposed by the trait, so they can be called.
When you change
fn do_thing() -> Box<u32>;
to
fn do_thing<T>() -> Box<T>;
it may look similar, but the behavior is much different. Let's take a normal function example
fn do_thing<T>(val: T) { }
fn main() {
do_thing(true);
do_thing(45 as u32);
}
the compiler performs what is a called monomorphization, which means your code in the compiler becomes essentially
fn do_thing_bool(val: bool) { }
fn do_thing_num(val: u32) { }
fn main() {
do_thing_bool(true);
do_thing_num(45 as u32);
}
The key thing to realize is that you are asking it to do the same thing for your trait. The problem is that the compiler can't do it. The example above relies on knowing ahead of time that do_thing is called with a number in one case and a boolean in another, and it can know with 100% certainty that those are the only two ways the function is used.
With your code
trait MyTrait {
fn do_thing<T>() -> Box<T>;
}
the compiler does not know what types do_thing will be called with, so it has no way to generate functions you'd need to call. To do that, wherever you convert the struct implementing Collidable into a boxed object it would have to know every possible return type get_ncollide_shape could have, and that is not supported.
Other links for this:
Understanding Traits and Object Safety
https://www.reddit.com/r/rust/comments/3an132/how_to_wrap_a_trait_object_that_has_generic/

How to take ownership of Any:downcast_ref from trait object?

I've met a conflict with Rust's ownership rules and a trait object downcast. This is a sample:
use std::any::Any;
trait Node{
fn gen(&self) -> Box<Node>;
}
struct TextNode;
impl Node for TextNode{
fn gen(&self) -> Box<Node>{
Box::new(TextNode)
}
}
fn main(){
let mut v: Vec<TextNode> = Vec::new();
let node = TextNode.gen();
let foo = &node as &Any;
match foo.downcast_ref::<TextNode>(){
Some(n) => {
v.push(*n);
},
None => ()
};
}
The TextNode::gen method has to return Box<Node> instead of Box<TextNode>, so I have to downcast it to Box<TextNode>.
Any::downcast_ref's return value is Option<&T>, so I can't take ownership of the downcast result and push it to v.
====edit=====
As I am not good at English, my question is vague.
I am implementing (copying may be more precise) the template parser in Go standard library.
What I really need is a vector, Vec<Box<Node>> or Vec<Box<Any>>, which can contain TextNode, NumberNode, ActionNode, any type of node that implements the trait Node can be pushed into it.
Every node type needs to implement the copy method, return Box<Any>, and then downcasting to the concrete type is OK. But to copy Vec<Box<Any>>, as you don't know the concrete type of every element, you have to check one by one, that is really inefficient.
If the copy method returns Box<Node>, then copying Vec<Box<Node>> is simple. But it seems that there is no way to get the concrete type from trait object.
If you control trait Node you can have it return a Box<Any> and use the Box::downcast method
It would look like this:
use std::any::Any;
trait Node {
fn gen(&self) -> Box<Any>; // downcast works on Box<Any>
}
struct TextNode;
impl Node for TextNode {
fn gen(&self) -> Box<Any> {
Box::new(TextNode)
}
}
fn main() {
let mut v: Vec<TextNode> = Vec::new();
let node = TextNode.gen();
if let Ok(n) = node.downcast::<TextNode>() {
v.push(*n);
}
}
Generally speaking, you should not jump to using Any. I know it looks familiar when coming from a language with subtype polymorphism and want to recreate a hierarchy of types with some root type (like in this case: you're trying to recreate the TextNode is a Node relationship and create a Vec of Nodes). I did it too and so did many others: I bet the number of SO questions on Any outnumbers the times Any is actually used on crates.io.
While Any does have its uses, in Rust it has alternatives.
In case you have not looked at them, I wanted to make sure you considered doing this with:
enums
Given different Node types you can express the "a Node is any of these types" relationship with an enum:
struct TextNode;
struct XmlNode;
struct HtmlNode;
enum Node {
Text(TextNode),
Xml(XmlNode),
Html(HtmlNode),
}
With that you can put them all in one Vec and do different things depending on the variant, without downcasting:
let v: Vec<Node> = vec![
Node::Text(TextNode),
Node::Xml(XmlNode),
Node::Html(HtmlNode)];
for n in &v {
match n {
&Node::Text(_) => println!("TextNode"),
&Node::Xml(_) => println!("XmlNode"),
&Node::Html(_) => println!("HtmlNode"),
}
}
playground
adding a variant means potentially changing your code in many places: the enum itself and all the functions that do something with the enum (to add the logic for the new variant). But then again, with Any it's mostly the same, all those functions might need to add the downcast to the new variant.
Trait objects (not Any)
You can try putting the actions you'd want to perform on the various types of nodes in the trait, so you don't need to downcast, but just call methods on the trait object.
This is essentially what you were doing, except putting the method on the Node trait instead of downcasting.
playground
The (more) ideomatic way for the problem:
use std::any::Any;
pub trait Nodeable {
fn as_any(&self) -> &dyn Any;
}
#[derive(Clone, Debug)]
struct TextNode {}
impl Nodeable for TextNode {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let mut v: Vec<Box<dyn Nodeable>> = Vec::new();
let node = TextNode {}; // or impl TextNode::new
v.push(Box::new(node));
// the downcast back to TextNode could be solved like this:
if let Some(b) = v.pop() { // only if we have a nodeā€¦
let n = (*b).as_any().downcast_ref::<TextNode>().unwrap(); // this is secure *)
println!("{:?}", n);
};
}
*) This is secure: only Nodeables are allowd to be downcasted to types that had Nodeable implemented.

Resources