Owned Trait Objects

We previously saw how trait objects can be used with references, e.g &dyn Pet. However, we can also use trait objects with smart pointers like Box to create an owned trait object: Box<dyn Pet>.

  1. struct Dog {
  2.     name: String,
  3.     age: i8,
  4. }
  5. struct Cat {
  6.     lives: i8,
  7. }
  8. trait Pet {
  9.     fn talk(&self) -> String;
  10. }
  11. impl Pet for Dog {
  12.     fn talk(&self) -> String {
  13.         format!("Woof, my name is {}!", self.name)
  14.     }
  15. }
  16. impl Pet for Cat {
  17.     fn talk(&self) -> String {
  18.         String::from("Miau!")
  19.     }
  20. }
  21. fn main() {
  22.     let pets: Vec<Box<dyn Pet>> = vec![
  23.         Box::new(Cat { lives: 9 }),
  24.         Box::new(Dog { name: String::from("Fido"), age: 5 }),
  25.     ];
  26.     for pet in pets {
  27.         println!("Hello, who are you? {}", pet.talk());
  28.     }
  29. }

Memory layout after allocating pets:

20.3. Owned Trait Objects - 图1

This slide should take about 10 minutes.

  • Types that implement a given trait may be of different sizes. This makes it impossible to have things like Vec<dyn Pet> in the example above.
  • dyn Pet is a way to tell the compiler about a dynamically sized type that implements Pet.
  • In the example, pets is allocated on the stack and the vector data is on the heap. The two vector elements are fat pointers:
    • A fat pointer is a double-width pointer. It has two components: a pointer to the actual object and a pointer to the virtual method table (vtable) for the Pet implementation of that particular object.
    • The data for the Dog named Fido is the name and age fields. The Cat has a lives field.
  • Compare these outputs in the above example:

    1. println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>());
    2. println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>());
    3. println!("{}", std::mem::size_of::<&dyn Pet>());
    4. println!("{}", std::mem::size_of::<Box<dyn Pet>>());