特征对象

特征(Trait)对象可接受不同类型的值,举例来说,在集合中会是这样:

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

以下是分配 pets 后的内存布局:

20.3. 特征对象 - 图1

This slide should take about 10 minutes.

  • 实现给定 trait 的类型可能大小不同。因此,上例中不可能具有像 Vec<dyn Pet> 这样的项。
  • 可通过“dyn Pet”这个方法向编译器告知实现“Pet”的动态大小类型。
  • 在本例中,pets 在栈上分配内存,矢量数据存储在堆上。这两个矢量元素是 胖指针
    • 胖指针属于全角指针。它包含两个部分:指向实际对象的指针,以及指向该特定对象的 Pet 实现的 虚拟方法表 (vtable) 的指针。
    • Dog(名为 Fido)类型的数据是 nameage 字段。Cat 类型包含一个 lives 字段。
  • 比较上述示例中的这些输出:

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