Consider the following code:
trait Animal {
fn make_sound(&self) -> String;
}
struct Cat;
impl Animal for Cat {
fn make_sound(&self) -> String {
"meow".to_string()
}
}
struct Dog;
impl Animal for Dog {
fn make_sound(&self) -> String {
"woof".to_string()
}
}
fn main () {
let dog: Dog = Dog;
let cat: Cat = Cat;
let v: Vec<Animal> = Vec::new();
v.push(cat);
v.push(dog);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}
The compiler tells me that v
is a vector of Animal
when I try to push cat
(type mismatch)
So, how can I make a vector of objects belonging to a trait and calls the corresponding trait method on each element?
You may use a reference trait object
&Animal
to borrow the elements and store these trait objects in aVec
. You can then enumerate it and use the trait's interface.Altering the
Vec
's generic type by adding a&
in front of the trait will work:This is great if you may want the original variable to keep ownership and reuse it later.
Keep in mind with the scenario above, you can't transfer ownership of
dog
orcat
because theVec
has borrowed these concrete instances at the same scope.Introducing a new scope can help handle that particular situation:
Vec<Animal>
is not legal, but the compiler can't tell you that because the type mismatch somehow hides it. If we remove the calls topush
, the compiler gives us the following error:The reason why that's not legal is that a
Vec<T>
stores manyT
objects consecutively in memory. However,Animal
is a trait, and traits have no size (aCat
and aDog
are not guaranteed to have the same size).To solve this problem, we need to store something that has a size in the
Vec
. The most straightforward solution is to wrap the values in aBox
, i.e.Vec<Box<Animal>>
.Box<T>
has a fixed size (a "fat pointer" if T is a trait, a simple pointer otherwise).Here's a working
main
: