Despacho Dinâmico
Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 45 de 61.
Quando você usa genéricos com limites de trait como <T: Summary>, o Rust determina o tipo exato em tempo de compilação. Isso é chamado de despacho estático—o compilador gera código especializado para cada tipo concreto que você utiliza. É rápido, mas há uma limitação: uma variável só pode conter um tipo específico.
E se você precisar de uma única variável que possa conter tipos diferentes em tempo de execução? É aqui que os trait objects entram. Usando Box<dyn Trait>, você pode armazenar qualquer tipo que implemente a trait:
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) -> String { String::from("Woof!") }
}
impl Speak for Cat {
fn speak(&self) -> String { String::from("Meow!") }
}
fn main() {
let animal: Box<dyn Speak> = Box::new(Dog);
println!("{}", animal.speak()); // "Woof!"
let animal: Box<dyn Speak> = Box::new(Cat);
println!("{}", animal.speak()); // "Meow!"
}
A palavra-chave dyn indica despacho dinâmico — o Rust procura qual método chamar em tempo de execução, em vez de tempo de compilação. O Box é necessário porque objetos de trait não possuem um tamanho conhecido; o Box fornece um ponteiro com um tamanho fixo.
Pense desta forma: os genéricos dizem "Eu trabalho com o tipo T", enquanto objetos de trait dizem "Eu trabalho com qualquer coisa que possa fazer isso". O tradeoff é um pequeno custo de runtime pela flexibilidade de lidar com diferentes tipos através da mesma variável.
Desafio
FácilVamos construir um sistema de aluguel de veículos que demonstra o poder do despacho dinâmico (dynamic dispatch)! Você criará uma trait que define o que qualquer veículo alugável pode fazer e, em seguida, usará Box<dyn Trait> para armazenar diferentes tipos de veículos na mesma variável.
Você organizará seu código em dois arquivos:
vehicle.rs: Defina uma trait públicaRentablecom um métodorental_infoque recebe&selfe retorna umaString. Em seguida, crie duas structs públicas:Car— com um campo públicomodel(String). Seurental_infodeve retornar"Car: {model}"Bike— com um campo públicobrand(String). Seurental_infodeve retornar"Bike: {brand}"
main.rs: Importe seu módulo vehicle e use as entradas para demonstrar o despacho dinâmico. Crie uma variávelBox<dyn Rentable>que primeiro contenha umCar, imprima suas informações de aluguel, depois reatribua a mesma variável para conter umaBikee imprima suas informações de aluguel novamente.
A mágica aqui é que uma única variável do tipo Box<dyn Rentable> pode conter tanto um Car quanto uma Bike — o Rust descobre qual método rental_info chamar em tempo de execução. Isso é o despacho dinâmico em ação!
Sua saída deve mostrar as informações de ambos os veículos:
Car: {model}
Bike: {brand}Por exemplo, com as entradas "Tesla Model 3" e "Trek":
Car: Tesla Model 3
Bike: TrekVocê receberá duas entradas: o modelo do carro e a marca da bicicleta.
Experimente você mesmo
mod vehicle;
use vehicle::{Rentable, Car, Bike};
fn main() {
// Ler entrada
let mut car_model = String::new();
std::io::stdin().read_line(&mut car_model).expect("Failed to read line");
let car_model = car_model.trim().to_string();
let mut bike_brand = String::new();
std::io::stdin().read_line(&mut bike_brand).expect("Failed to read line");
let bike_brand = bike_brand.trim().to_string();
// TODO: Criar uma variável Box<dyn Rentable> que contenha um Car
// Imprimir suas informações de aluguel usando o método rental_info()
// TODO: Reatribuir a mesma variável para conter uma Bike
// Imprimir suas informações de aluguel novamente
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Métodos e Comportamento
Introdução a Blocos de ImplementaçãoO Parâmetro SelfMétodos MutáveisFunções AssociadasMúltiplos Blocos de ImplementaçãoEncadeamento de MétodosRecapitulação - Ações de Retângulo4Projeto: Pet Virtual
Definindo o PetAlimentando o Pet7Traits Padrão
A Trait DebugA Trait DisplayClone e CopyTraits de IgualdadeRecapitulação - Ponto Imprimível10Projeto: Sistema de Documentos
A Trait DrawComponente de Texto2Encapsulamento e Módulos
Fundamentos de MódulosA Palavra-chave PublicCampos PrivadosGettersSettersRecapitulação - Cofre Seguro5Genéricos
Structs GenéricasMétodos GenéricosMúltiplos Tipos GenéricosFunções GenéricasRecapitulação - Ponto de Coordenada8Traits como Restrições
Sintaxe de Restrições de TraitRestrições MúltiplasA Cláusula WhereRetornando Tipos com TraitsRecapitulação - Impressora Genérica11Padrões de Projeto em Rust
Padrão NewtypeComposiçãoA Trait DropFrom e IntoRecapitulação - Mock de Smart Pointer3Enums Avançados
Enums com DadosMétodos em EnumsCorrespondência de Variantes de DadosO Enum Option RevisitadoRecapitulação - Enum Shape6Definição de Traits
O que é uma Trait?Implementando TraitsImplementações PadrãoSobrescrevendo PadrõesTraits com ParâmetrosRecapitulação - Media Player9Objetos Trait
Despacho DinâmicoVetores de TraitsIterando Objetos TraitRecapitulação - Gerenciador de Zoológico