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>'
- How can I give the compiler a hint or explicitly tell the compiler that
foo
(Foo
) implementsBar
(B: Bar
)? - Is this a bug? Should I just hold off on using Rust until it reaches 1.0?
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 functionB
by any type implementing the traitBar
".For example, if you created a struct
Foo3
implementing traitBar
as well, the compiler would expect to be able to calldo_something
withB
beingFoo3
, which is not possible with your current implementation.In your situation, your
do_something
function attempts to create aB
object, it thus needs a generic way to do so, given by theBar
trait, as acreate_bar()
method for example, like this :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 thatcreate_bar
does not take any arguments.But something like this would work without any problem :
The point is : your
do_something
function cannot createBar
objects without a generic way of doing so, a way that would not depend on which type is in<B>
, provided it implementsBar
. That's how generics work : if you calldo_something::<Foo2>()
, it's exactly as if you replacedB
byFoo2
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 :Basically, Trait objects are references or pointers to objects, casted as Trait :
And as provided in my example, it works with Boxes as well.