Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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ública Rentable com um método rental_info que recebe &self e retorna uma String. Em seguida, crie duas structs públicas:
    • Car — com um campo público model (String). Seu rental_info deve retornar "Car: {model}"
    • Bike — com um campo público brand (String). Seu rental_info deve retornar "Bike: {brand}"
  • main.rs: Importe seu módulo vehicle e use as entradas para demonstrar o despacho dinâmico. Crie uma variável Box<dyn Rentable> que primeiro contenha um Car, imprima suas informações de aluguel, depois reatribua a mesma variável para conter uma Bike e 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: Trek

Você 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
    
}
quiz iconTeste seus conhecimentos

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