Use Trait as Vec Type - rust

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/

Related

Rust can't find trait implementation

I'm trying to implement the Add trait for anything that implements another trait (in the example code the Test trait). I'm using references in the Add implementation because not everything that implements Test will be the same size. The code below compiles fine:
use std::ops::Add;
struct Foo(i32);
struct Bar(i64);
trait Test {}
impl Test for Foo {}
impl Test for Bar {}
impl<'a, 'b> Add<&'b Test> for &'a Test {
type Output = Box<Test>;
fn add(self, other: &'b Test) -> Box<Test> {
if true {
Box::new(Foo(5))
} else {
Box::new(Bar(5))
}
}
}
When I try to actually use Add, as below, it says that the operation could not be applied because the implementation of Add for &Foo is missing.
fn test_add() {
&Foo(5) + &Bar(5)
}
Have I defined the implementation incorrectly? Have I called it incorrectly? The goal is to make the function add take two references to objects which both implement Test, and return a reference (or box) to a new object that implements Test (and might not be the same underlying type as either of the inputs).
I found another approach that changes the behavior slightly, but works.
struct V<T>(T);
use std::ops::Add;
impl<T1: Test, T2: Test> Add<V<Box<T2>>> for V<Box<T1>> {
type Output = V<Box<Test>>;
fn add(self, other: V<Box<T2>>) -> Self::Output {
unimplemented!()
}
}
That allows it to return any type that implements Test, at the cost of wrapping everything in a Box and a dummy struct V. Not very elegant, and I still don't understand why my original code doesn't work, but at least this has the behavior I wanted.
The problem is that the compiler cannot implicitly convert from &Foo into &Test. If you explicitly convert it into &Test first, then the operator overloading works:
fn test_add() {
let foo: &Test = &Foo(5);
foo + &Bar(5);
}
Alternatively, you can use the fully qualified syntax:
fn test_add() {
<&Test as Add<&Test>>::add(&Foo(5), &Bar(5));
}

Rust subtyping/"runtime polymorphism"/using traits in Boxes

Consider the following dumbed-down example:
pub trait ThemePark<A, V>
where
A: Attraction,
V: Visitor,
{
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<A>;
fn get_visitors(&self) -> Vec<V>;
}
pub trait Attraction {}
pub trait Visitor {}
A concrete ThemePark could have an arbitrary set of attractions, as well as visitors. They are implemented with structs:
struct ElderlyWhiteMale;
impl Visitor for ElderlyWhiteMale {}
A ThemePark is wrapped in some company's assets, like so:
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
This begins my pain. I put ThemePark in a Box because I don't know the size of it at compile time. It could be wrapped around any kind of Attraction and Visitor.
ThemePark needs 2 type arguments, but I can't know them at compile-time. Somewhere in my code I read this from an external file and build a ThemePark accordingly.
The idea is, that at runtime I can create a ThemePark from an external source and then invoke the functions defined in the trait on it.
impl Asset {
fn init_and_query() -> () {
let theme_park: Box<ThemePark> = match external_file.get_theme_park_type {
ThemeParkType::FUN => unimplemented! {"There is no fun, yet!"},
ThemeParkType::SERIOUS => {
println!("Creating serious themepark");
SeriousThemePark::create(size /*...*/)
}
};
let attractions = theme_park.get_attractions();
// ... Do something with the attractions
}
}
pub enum ThemeParkType {
FUN,
SERIOUS,
}
I understand that I can't put the ThemePark as-is on the stack... it's size is unknown at compile time, so the compiler can't know what to allocate.
That's why I either use a reference & or wrap it in a Box like I do here.
I understand there is type erasure, meaning that I would get back only a ThemePark and not a SeriousThemePark, but that would suffice for the moment.
Am I using traits all wrong here? How would you go and fix that. Coming from Java/Scala/C++ I seem to be stuck too deep in existing thinking.
Rust polymorphism is very similar to C++ polymorphism in that regard; it features:
compile-time polymorphism, to parameterize an item with types known at compile-time,
run-time polymorphism, when the concrete types are not known at compile-time.
Rust uses trait to define an interface which is then used to both constrain compile-time type parameters and serve as base-class/interface for run-time polymorphism, which is perhaps where your confusion comes from, however both kinds of polymorphism are inherently different.
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
Then you should NOT be using compile-time polymorphism, and instead define ThemePark as:
pub trait ThemePark {
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<Box<Attraction>>;
fn get_visitors(&self) -> Vec<Box<Visitor>>;
}
By instantiating a ThemePark<A, V> you create a theme park which can only ever contain one type of attractions (it's all Haunted Houses here, no Flume Ride sorry!) and one type of visitors (only Elderly Guys in, no Elderly Ladies or Kids).

Static method in trait dynamic dispatch

Trying to get dynamic dispatch working in a trait static method but get a type must be known error.
I'm trying to achieve something like
F#
https://github.com/Thorium/SimpleCQRS-FSharp/blob/master/CommandSide/Domain.fs
C#
https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/Domain.cs..
Is the only way to make the trait generic?
pub struct Aggregate<T: AggregateRoot>
{
pub id: Uuid,
agg: T,
changes: Vec<Box<Any>>
}
impl <T :AggregateRoot > Aggregate<T>
{
fn GetUncomittedChanges(&self) -> Vec<Box<Any>> { self.changes}
fn MarkChangesAsCommitted(&self) { self.changes.drain(..);}
}
trait AggregateRoot
{
fn new2() -> Self; //should be private
fn new(id: Uuid) -> Self;
fn LoadsFromHistory(changes : Vec<Box<Any>> ) -> Self
where Self: Sized
{
let newAgg = AggregateRoot::new2 ();
changes.iter().map( |e| newAgg.Apply(e) );
newAgg.MarkChangesAsCommitted();
newAgg
}
fn Apply<U: Any>(&self, arg: U) ;
fn GetId(&self) -> Uuid;
}
currently trying but gives 2 params expected 1 supplied.
Let's start with issues in how you asked the question, in the hopes that you will be able to ask better questions in the future. The complete error you are getting is:
<anon>:27:37: 27:52 error: the type of this value must be known in this context
<anon>:27 changes.iter().map( |e| newAgg.Apply(e) );
^~~~~~~~~~~~~~~
Note that the compiler error message shows you exactly which bit of code is at fault. It's useful to include that error when asking a question.
You've also included extraneous detail. For example, GetUncomittedChanges, id and GetId are all unused in your example. When solving a problem, you should produce an MCVE. This helps you understand the problem better and also allows people helping you to look at less code which usually results in faster turnaround.
Your code has a number of problems, but let's start at the first error:
let newAgg = AggregateRoot::new2 ();
This says "for any possible AggregateRoot, create a new one". Many concrete types can implement a trait (which is the point of traits), but the compiler needs to know how much space to allocate for a given instance. There might be a struct that takes 1 byte or 200 bytes; how much space needs to be allocated on the stack in this case?
To progress, you can use Self::new2 instead. That means to create a new instance of the current implementor.
The next error is
<anon>:20:16: 20:40 error: no method named `MarkChangesAsCommitted` found for type `Self` in the current scope
<anon>:20 newAgg.MarkChangesAsCommitted();
^~~~~~~~~~~~~~~~~~~~~~~~
You are calling a method on a concrete type from a trait implementation; this simply doesn't make any sense. What would happen if a bool implements this trait? It doesn't have a MarkChangesAsCommitted method. I don't know what you intended in this case, so I'll just delete it.
Now you get this error:
<anon>:19:9: 19:16 error: `changes` does not live long enough
<anon>:19 changes.iter().map( |e| newAgg.Apply(e) );
^~~~~~~
note: reference must be valid for the static lifetime...
<anon>:17:5: 21:6 note: ...but borrowed value is only valid for the scope of parameters for function at 17:4
That's because your method Apply expects to be given a type that implements Any. However, you are passing a &Box<Any>. Any has a lifetime bound of 'static, and that reference is not static. A straightforward change is to accept a reference to a type that implements Any:
fn Apply<U: Any>(&self, arg: &U);
Now that the code compiles, there's a number of stylistic issues to fix:
no space before :
no space after >
no space before (
no space inside ()
map should not be used for side effects
function and variable names are camel_case
most of the time, accept a &[T] instead of a Vec<T> as a function argument.
use "Egyptian" braces, except when you are using a where clause.
All together, your code looks like:
use std::any::Any;
struct Aggregate<T: AggregateRoot> {
agg: T,
changes: Vec<Box<Any>>
}
impl<T: AggregateRoot> Aggregate<T> {
fn mark_changes_as_committed(&self) { }
}
trait AggregateRoot {
fn new() -> Self;
fn load_from_history(changes: &[Box<Any>]) -> Self
where Self: Sized
{
let new_agg = Self::new();
for change in changes { new_agg.apply(change) }
new_agg
}
fn apply<U: Any>(&self, arg: &U);
}
fn main() {}
Is there a way to constrain the concrete types of the AggregateRoot to Aggregates so mark_changes can be called?
Not that I'm aware of. It sounds like you want to move mark_changes to the trait and force all implementors of the trait to implement it:
trait AggregateRoot {
fn load_from_history(changes: &[Box<Any>]) -> Self
where Self: Sized
{
let new_agg = Self::new();
for change in changes { new_agg.apply(change) }
new_agg.mark_changes_as_committed();
new_agg
}
fn mark_changes_as_committed(&self);
// ...
}

Lifetimes in traits

I'm currently diving into rust and writing a little math library.
The problem I like to solve is quite simple, I want to model fields and rings, but I can't get the lifetimes right.
here's the code:
ring.rs:
pub trait Ring {
fn characteristic() -> int;
fn is_unit(&self) -> bool;
fn is_field() -> bool;
}
field.rs:
use ring::Ring;
pub trait Field : Ring {
fn some_field_method() -> bool {
true
}
}
impl Ring for Field {
fn is_field() -> bool {
true
}
}
when compiling I get the following error:
/src/field.rs:9:15: 9:20 error: explicit lifetime bound required
/src/field.rs:9 impl Ring for Field {
^~~~~
I read the rust documentation, with the lifetime section and the rust-by-example section about it. The motivation behind lifetimes is obvious to me and I understand all the given examples. But here, I'm totally lost.
Btw: this is the minified version, I tried giving the Field a named lifetime, also the impl and Ring and various combinations of said.
May anyone explain what is happening here, or, if this is too specific, how to work with lifetimes and traits.
Thanks
You are trying to do some king of inheritance-like pattern with trait, which is not really how they work.
You can think of traits as somehow similar to interfaces provided by some languages : they are only a guaranty that your struct will provide some methods.
The syntax trait Foo : Bar does not mean that somehow the trait Foo is a superset of trait Bar, and that implementing Foo for a struct will implement Bar as well. It only states that trait Foo can only be implemented for structs already implementing trait Bar. You still need to implement both traits yourself.
Following your example, your approach would be something like this :
First, define the trait Ring:
pub trait Ring {
fn characteristic(&self) -> int;
fn is_unit(&self) -> bool;
fn is_field(&self) -> bool {
// Default value : not every Ring is a Field
false
}
}
Then, the trait Field, which requires the trait Ring
pub trait Field : Ring {
fn some_field_method(&self) -> bool {
// This is a default value as well
true
}
}
Then, your struct which will be both a Ring and a Field
struct MyField;
// First, implement Ring for MyField
impl Ring for MyField {
fn characteristic(&self) -> int {
2i
}
fn is_unit(&self) -> bool {
false
}
fn is_field(&self) -> bool {
// We override the default value : MyField is a Field
true
}
}
// Then we can implement Field for MyField
impl Field for MyField {
// Nothing here if we keep default implementation of some_field_method
}
Now, some explanations about this strange error with lifetimes.
When you wrote impl Ring for Field {...}, you where actually trying to implement the trait Ring for trait object Field.
A trait object is what you get when you use a reference to a struct like a reference to one of its traits, and they are quite peculiar to use, and require some playing with lifetimes.
However, in most situation you won't need to play with them, and classic generics will suffice.
You can have a look to my answer here, where I explain it with more details.

Pass boxed trait as a reference

So I have a trait that I want to pass around and do some dynamic method dispatching on. I have one method that needs the trait as a boxed pointer, but it needs to call another method that uses a reference to the trait. So something like:
trait Foo {
fn do_something(&self);
}
struct Bar;
impl Foo for Bar {
fn do_something(&self) {}
}
fn foo_as_box(foo : Box<Foo>) {
foo_as_ref(&foo);
}
fn foo_as_ref(foo : &Foo) {
foo.do_something();
}
fn main() {
let boxed_foo = box Bar as Box<Foo>;
foo_as_box(boxed_foo);
}
But, I get an error on this code
error: failed to find an implementation of trait Foo for Box Foo<no-bounds>
The compiler will convert Box<Foo> into &Foo as needed automatically. You can just say
foo_as_ref(foo);
and it will work.
This doesn't necessarily work in more complex situations. Which is to say, the compiler can't always tell that you want that conversion.
Now that DST has been implemented, you can say &*foo to do the conversion.
In the past the only way to force it was a let-binding, as in
let ref_foo: &Foo = foo;
Of course, your use-case is simple enough that you don't need this.

Resources