Владеющие объекты трейтов
Ранее мы видели, как объекты трейтов можно использовать со ссылками, например &dyn Pet. Однако объекты трейтов можно использовать и с умными указателями, такими как Box, чтобы создать владеющий объект трейта: Box<dyn Pet>.
// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0
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!("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:
This slide should take about 10 minutes.
- Типы, которые реализуют данный трейт, могут иметь разные размеры. Поэтому, как в примере выше, невозможно иметь что-то вроде
Vec<dyn Pet>. dyn Pet— способ сообщить компилятору о типе динамического размера, который реализуетPet.- В примере
petsвыделен на стеке, а данные вектора — в куче. Два элемента вектора — толстые указатели (fat pointers):- Толстый указатель — это указатель двойной ширины. Он состоит из двух компонентов: указателя на сам объект и указателя на таблицу виртуальных методов (vtable) для реализации
Petэтого конкретного объекта. - Данные для
Dogпо имени Fido — это поляnameиage. УCatесть полеlives.
- Толстый указатель — это указатель двойной ширины. Он состоит из двух компонентов: указателя на сам объект и указателя на таблицу виртуальных методов (vtable) для реализации
- Сравните эти выводы в примере выше:
// Copyright 2024 Google LLC // SPDX-License-Identifier: Apache-2.0 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>>());