I asked a similar question earlier which has helped me understand what is going on under the hood but I still can not get Rust to do what I want it to do when it comes to generic programming. Here's some code:
struct Foo<B: Bar> { bars: Vec<Box<B>> }
struct Foo2;
trait Bar {}
impl Bar for Foo2 {}
impl<B: Bar> Foo<B> {
fn do_something() -> Foo<B> {
let foo2:Box<Bar> = box Foo2;
let mut foo = Foo { bars: vec!(box Foo2) };
foo.bars.push(box Foo2);
foo // compiler: *ERROR*
}
}
Error: expected 'Foo<B>', found 'Foo<Foo2>'
foo
(Foo
) implements Bar
(B: Bar
)?version: 0.12.0-nightly (4d69696ff 2014-09-24 20:35:52 +0000)
Problems I see with @Levans' solution:
struct Foo2;
struct Foo3 {
a: int
}
trait Bar {
fn create_bar() -> Self;
}
impl Bar for Foo2 {
fn create_bar() -> Foo2 { Foo2 } // will work
}
impl Bar for Foo3 {
fn create_bar(a: int) -> Foo3 { Foo3 {a: a} } // will not work
}
Error: method 'create_bar' has 1 parameter but the declaration in trait 'Bar::create_bar' has 0
Also, I noticed this: Bar::create_bar()
. How would Rust know to use Foo2
's implementation?
When you define a function using <B: Bar>
you're telling the compiler "you can replace in this function B
by any type implementing the trait Bar
".
For example, if you created a struct Foo3
implementing trait Bar
as well, the compiler would expect to be able to call do_something
with B
being Foo3
, which is not possible with your current implementation.
In your situation, your do_something
function attempts to create a B
object, it thus needs a generic way to do so, given by the Bar
trait, as a create_bar()
method for example, like this :
struct Foo<B: Bar> { bars: Vec<Box<B>> }
struct Foo2;
trait Bar {
fn create_bar() -> Self;
}
impl Bar for Foo2 {
fn create_bar() -> Foo2 { Foo2 }
}
impl<B: Bar> Foo<B> {
fn do_something() -> Foo<B> {
let mut foo = Foo { bars: vec!(box Bar::create_bar()) };
foo.bars.push(box Bar::create_bar());
foo
}
}
Answer to edit :
In your code, it indeed won't work because you expect to pass more arguments to create_bar
, which is not possible as it does not respect the trait definition that create_bar
does not take any arguments.
But something like this would work without any problem :
struct Foo2;
struct Foo3 {
a: int
}
trait Bar {
fn create_bar() -> Self;
}
impl Bar for Foo2 {
fn create_bar() -> Foo2 { Foo2 }
}
impl Bar for Foo3 {
fn create_bar() -> Foo3 { Foo3 {a: Ou} }
}
The point is : your do_something
function cannot create Bar
objects without a generic way of doing so, a way that would not depend on which type is in <B>
, provided it implements Bar
. That's how generics work : if you call do_something::<Foo2>()
, it's exactly as if you replaced B
by Foo2
in the whole definition of your function.
Yet, I suspect what you really are trying to do is store differents types, all implementing Bar
in the same Vec, (otherwise wrapping a Box inside would be quite useless), you can achieve this with trait objects, and it does not require generics :
struct Foo<'a> { bars: Vec<Box<Bar + 'a>> }
struct Foo2;
trait Bar {}
impl Bar for Foo2 {}
impl<'a> Foo<'a> {
fn do_something() -> Foo<'a> {
let mut foo = Foo { bars: vec!(box Foo2 as Box<Bar>) };
foo.bars.push(box Foo2 as Box<Bar>);
foo
}
}
Basically, Trait objects are references or pointers to objects, casted as Trait :
let foo2 = Foo2;
let bar = &foo2 as &Bar; // bar is a reference to a Trait object Bar
And as provided in my example, it works with Boxes as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With