The existing answers explain the problem with Vec<Animal>
well, but they use older syntax, which is not valid anymore.
In short, the vector needs to contain trait objects and its type should be (something like) Vec<Box<dyn Animal>>
.
In modern Rust, the dyn
keyword is used to specify a trait object. But we cannot use just Vec<dyn Animal>
, because dyn Animal
is not sized (Cat
and Dog
could pottentially have fields of different size). Vectors can only contain elements of a fixed size. So that's why in the vector we should rather store some sort of pointers to the actual structs. The Box
struct is one such option, a kind of a smart pointer that has a fixed size in itself.
Let's test this (on a 64-bit machine):
use std::mem::size_of;
println!("size Cat = {}", size_of::<Cat>()); // 0 bytes (the Cat struct has no fields)
println!("size Dog = {}", size_of::<Dog>()); // 0 bytes (the Dog struct has no fields)
println!("size BoxCat = {}", size_of::<Box<Cat>>()); // 8 bytes (1 usize pntr)
println!("size BoxDyn = {}", size_of::<Box<dyn Animal>>()); // 16 bytes (2 usize pointers)
println!("{}", size_of::<dyn Animal>()); // Error: doesn't have a size known at compile-time
Note that if Cat
had fields, size_of::<Cat>()
would have been more than 0
, but size_of::<Box<Cat>>()
and size_of::<Box<dyn Animal>>()
wouldn't change at all.
Also note that Box<dyn Animal>
actually contains 2 pointers:
- one that points to the actual struct instance data;
- one for the vtable (that's because of
dyn
; it's needed for dynamic dispatching).
Now to your example. To make it work, you just need to replace these three lines:
let v: Vec<Animal> = Vec::new();
v.push(cat);
v.push(dog);
with these:
let mut v: Vec<Box<dyn Animal>> = Vec::new();
v.push(Box::new(cat));
v.push(Box::new(dog));