dyn Trait
Além de usar traits para despacho estático via genéricos, o Rust também suporta usá-los para despacho dinâmico, apagamento de tipo, via objetos de trait:
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!("Auau, meu nome é {}", self.name) } } impl Pet for Cat { fn talk(&self) -> String { String::from("Miau!") } } // Usa genéricos e despacho estático. fn generic(pet: &impl Pet) { println!("Olá, quem é você? {}", pet.talk()); } // Usa apagamento de tipo e despacho dinâmico. fn dynamic(pet: &dyn Pet) { println!("Olá, quem é você? {}", pet.talk()); } fn main() { let cat = Cat { lives: 9 }; let dog = Dog { name: String::from("Bidu"), age: 5 }; generic(&cat); generic(&dog); dynamic(&cat); dynamic(&dog); }
-
Os genéricos, incluindo
impl Trait
, usam a monomorfização para criar uma instância especializada da função para cada tipo diferente com o qual o genérico é instanciado. Isso significa que chamar um método de trait de dentro de uma função genérica ainda usa despacho estático, pois o compilador tem todas as informações de tipo e pode resolver qual tipo de implementação do trait ele deverá utilizar. -
Quando se usa
dyn Trait
, ele usa despacho dinâmico através de uma tabela de métodos virtuais (vtable). Isso significa que há uma única versão defn dynamic
que é usada independentemente do tipo dePet
que é passado. -
Quando se usa
dyn Trait
, o objeto de trait precisa estar atrás de algum tipo de indireção. Neste caso, é uma referência, embora tipos de ponteiro inteligente (smart comoBox
também possam ser usados (isso será demonstrado no dia 3). -
Em tempo de execução, um
&dyn Pet
é representado como um “ponteiro gordo”, ou seja, um par de dois ponteiros: Um ponteiro aponta para o objeto concreto que implementaPet
, e o outro aponta para a tabela de métodos virtuais para a implementação do trait para esse tipo. Ao chamar o métodotalk
em&dyn Pet
, o compilador procura o ponteiro de função paratalk
na tabela de métodos virtuais e então invoca a função, passando o ponteiro para oDog
ouCat
para essa função. O compilador não precisa saber o tipo concreto doPet
para fazer isso. -
Um
dyn Trait
é considerado “apagado de tipo”, porque não temos mais conhecimento em tempo de compilação sobre qual é o tipo concreto.