Принадлежні об'єкти трейтів
Раніше ми бачили, як об'єкти трейтів можна використовувати з посиланнями, наприклад, &dyn Pet
. Однак, ми також можемо використовувати об'єкти трейтів з розумними вказівниками, такими як Box
, щоб створити власний об'єкт трейту: Box<dyn Pet>
.
struct Dog { name: String, age: i8, } struct Cat { lives: i8, } trait Pet { fn talk(&self) -> String; } impl Pet for Dog { fn talk(&self) -> String { format!("Гав, мене звуть {}!", self.name) } } impl Pet for Cat { fn talk(&self) -> String { String::from("Мяу!") } } fn main() { let pets: Vec<Box<dyn Pet>> = vec![ Box::new(Cat { lives: 9 }), Box::new(Dog { name: String::from("Фідо"), age: 5 }), ]; for pet in pets { println!("Привіт, ви хто? {}", pet.talk()); } }
Розташування пам’яті після виділення pets
:
This slide should take about 10 minutes.
- Типи, що реалізують певний трейт, можуть бути різних розмірів. Це унеможливлює створення таких типів, як
Vec<dyn Pet>
у наведеному вище прикладі. dyn Pet
— це спосіб повідомити компілятору про тип динамічного розміру, який реалізуєPet
.- У прикладі
pets
розміщується у стеку, а векторні дані - у купі. Два векторні елементи є жирними вказівниками:- Жирний вказівник - це вказівник подвійної ширини. Він складається з двох компонентів: вказівника на власне об'єкт і вказівника на таблицю віртуальних методів (vtable) для реалізації
Pet
цього конкретного об'єкта. - Дані для
Dog
на ім'я Фідо - це поляname
таage
. ДляCat
є полеlives
.
- Жирний вказівник - це вказівник подвійної ширини. Він складається з двох компонентів: вказівника на власне об'єкт і вказівника на таблицю віртуальних методів (vtable) для реалізації
- Порівняйте ці результати в наведеному вище прикладі:
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>>());