dyn Trait
На додаток до використання трейтів для статичного пересилання за допомогою узагальнень, Rust також підтримує їх використання для динамічного пересилання зі стиранням типу за допомогою об’єктів трейтів:
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 generic(pet: &impl Pet) {
println!("Привіт, ви хто? {}", pet.talk());
}
// Використовує стирання типів та динамічну диспетчеризацію.
fn dynamic(pet: &dyn Pet) {
println!("Привіт, ви хто? {}", pet.talk());
}
fn main() {
let cat = Cat { lives: 9 };
let dog = Dog { name: String::from("Фідо"), age: 5 };
generic(&cat);
generic(&dog);
dynamic(&cat);
dynamic(&dog);
}
-
Узагальнення, включаючи
impl Trait, використовують мономорфізацію для створення спеціалізованого екземпляру функції для кожного окремого типу, який є екземпляром узагальнення. Це означає, що виклик методу трейта з узагальненої функції все ще використовує статичну диспетчеризацію, оскільки компілятор має повну інформацію про тип і може вирішити, яку саме реалізацію трейта типу слід використовувати. -
При використанні
dyn Traitзамість цього використовується динамічна диспетчеризація через віртуальну таблицю методів (vtable). Це означає, що існує єдина версіяfn dynamic, яка використовується незалежно від того, який типPetпередано. -
При використанні
dyn Traitоб’єкт трейта повинен знаходитися за якимось посередником. У цьому випадку це буде посилання, хоча також можна використовувати розумні типи вказівників, такі якBox(це буде продемонстровано у день 3). -
Під час виконання
&dyn Petпредставляється як “жирний вказівник”, тобто пара з двох вказівників: Один вказівник вказує на конкретний об’єкт, який реалізуєPet, а інший вказує на таблицю vtable для реалізації трейту для цього типу. При виклику методуtalkна&dyn Petкомпілятор шукає вказівник на функціюtalkу таблиці vtable, а потім викликає цю функцію, передаючи вказівник наDogабоCatу цю функцію. Для цього компілятору не потрібно знати конкретний типPet. -
dyn Traitвважається “стертим типом”, оскільки під час компіляції ми більше не знаємо, яким є конкретний тип.