特征(Trait)对象

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

struct Dog { name: String, age: i8 }
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.

trait Pet {
    fn talk(&self) -> String;
}

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

impl Pet for Cat {
    fn talk(&self) -> String { String::from("Miau!") }
}

fn main() {
    let pets: Vec<Box<dyn Pet>> = vec![
        Box::new(Cat { lives: 9 }),
        Box::new(Dog { name: String::from("Fido"), age: 5 }),
    ];
    for pet in pets {
        println!("Hello, who are you? {}", pet.talk());
    }
}

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

<Dog as Pet>::talk<Cat as Pet>::talkStackHeappetsFidoptrlen2capacity2dataname,4,4age5vtabledatalives9vtable
  • 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”这个方法向编译器告知实现“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.
  • 比较上述示例中的这些输出:
        println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>());
        println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>());
        println!("{}", std::mem::size_of::<&dyn Pet>());
        println!("{}", std::mem::size_of::<Box<dyn Pet>>());