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. ```
   }
   ```

9. ```
   trait Pet {
   ```
10. ```
        fn talk(&self) -> String;
    ```
11. ```
    }
    ```

13. ```
    impl Pet for Dog {
    ```
14. ```
        fn talk(&self) -> String {
    ```
15. ```
            format!("Woof, my name is {}!", self.name)
    ```
16. ```
        }
    ```
17. ```
    }
    ```

19. ```
    impl Pet for Cat {
    ```
20. ```
        fn talk(&self) -> String {
    ```
21. ```
            String::from("Miau!")
    ```
22. ```
        }
    ```
23. ```
    }
    ```

25. ```
    fn main() {
    ```
26. ```
        let pets: Vec&lt;Box&lt;dyn Pet>> = vec![
    ```
27. ```
            Box::new(Cat { lives: 9 }),
    ```
28. ```
            Box::new(Dog { name: String::from("Fido"), age: 5 }),
    ```
29. ```
        ];
    ```
30. ```
        for pet in pets {
    ```
31. ```
            println!("Hello, who are you? {}", pet.talk());
    ```
32. ```
        }
    ```
33. ```
    }
    ```

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>>());