Despacho Dinâmico
Parte da seção Programação Orientada a Objetos do Journey de Rust da Coddy. Lição 45 de 61.
Quando você usa genéricos com limites de trait, como <T: Summary>, 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ê usa. É 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 entram os objetos de trait. Usando Box<dyn Trait>, você pode armazenar qualquer tipo que implemente o 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 determina em tempo de execução qual método chamar, em vez de fazê-lo em tempo de compilação. O Box é necessário porque objetos de trait não têm um tamanho conhecido; Box fornece um ponteiro com tamanho fixo.
Pense desta forma: os genéricos dizem "trabalho com o tipo T", enquanto os objetos de trait dizem "trabalho com qualquer coisa que possa fazer isto". A compensação é um pequeno custo em tempo de execução pela flexibilidade de lidar com tipos diferentes por meio da mesma variável.
Desafio
FácilVamos criar um sistema de aluguel de veículos que demonstra o poder do despacho dinâmico! Você criará um 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 um trait públicoRentablecom um métodorental_infoque recebe&selfe retorna umaString. Em seguida, crie duas structs públicas:Car: com um campo públicomodel(String). Seurental_infodeve retornarCar: {model}Bike: com um campo públicobrand(String). Seurental_infodeve retornarBike: {brand}
main.rs: Inclua seu módulo de veículos 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 umBikee imprima novamente suas informações de aluguel.
A mágica aqui é que uma única variável do tipo Box<dyn Rentable> pode conter tanto um Car quanto um 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 dos dois 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: Crie uma variável Box<dyn Rentable> que contenha um Car
// Imprima suas informações de aluguel usando o método rental_info()
// TODO: Reatribua a mesma variável para conter um Bike
// Imprima 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 Programação Orientada a Objetos
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ógicoPratique por conta própria: Compilador de Rust online