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
вважається "стертим типом", оскільки під час компіляції ми більше не знаємо, яким є конкретний тип.