Iterando Objetos Trait
Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 47 de 61.
Agora que você pode armazenar diferentes tipos em um Vec<Box<dyn Trait>>, o próximo passo é processar essa coleção. Iterar sobre objetos de trait funciona exatamente como iterar sobre qualquer vetor — você percorre cada elemento e chama métodos definidos pela trait.
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}
impl Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
}
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 2.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
Box::new(Circle { radius: 1.0 }),
];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}
Quando você itera com for shape in &shapes, cada shape é uma referência a um Box<dyn Shape>. Você pode chamar qualquer método da trait Shape diretamente — o Rust lida com o despacho dinâmico automaticamente, chamando a implementação correta para cada tipo concreto.
Este padrão é poderoso para agregar resultados. Você poderia somar todas as áreas, filtrar formas por tamanho ou transformar a coleção. A principal percepção é que, uma vez que os itens estão em uma coleção de objetos trait, você interage com eles puramente através da interface da trait, independentemente de seus tipos subjacentes.
Desafio
FácilVamos construir uma calculadora de preços para um carrinho de compras que pode conter diferentes tipos de itens! Você criará um sistema onde produtos e serviços — cada um com uma lógica de precificação diferente — podem ser armazenados juntos e processados para calcular um preço total.
Você organizará seu código em dois arquivos:
items.rs: Defina uma trait públicaPriceablecom um métodopriceque recebe&selfe retorna umf64. Em seguida, crie duas structs públicas:Product— com campos públicosname(String) ecost(f64). Seupricedeve simplesmente retornar ocost.Service— com campos públicosname(String),hourly_rate(f64) ehours(f64). Seupricedeve retornarhourly_rate * hours.
main.rs: Importe seu módulo de itens e crie um vetor do tipoVec<Box<dyn Priceable>>. Use as entradas para criar um Product e um Service, adicione-os ao seu vetor, então itere pela coleção para calcular e imprimir o preço total de todos os itens.
O poder dos trait objects brilha aqui — seu código de iteração não precisa saber se está processando um Product ou um Service. Ele simplesmente chama price() em cada item, e o Rust despacha para a implementação correta automaticamente.
Sua saída deve exibir o preço total com uma casa decimal:
Total: ${total}Por exemplo, com as entradas Laptop, 999.99, Consulting, 150.0 e 3.0:
Total: $1449.99E com as entradas Book, 29.99, Tutoring, 50.0 e 2.0:
Total: $129.99Você receberá cinco entradas: o nome do produto, o custo do produto, o nome do serviço, o valor da hora e as horas trabalhadas (converta os valores numéricos como f64).
Folha de consulta
Para iterar sobre uma coleção de objetos trait, use um loop for padrão com uma referência para o vetor:
for item in &collection {
item.trait_method();
}
Ao iterar com for item in &vec, cada item é uma referência a um Box<dyn Trait>. Você pode chamar qualquer método da trait diretamente — o Rust lida com o despacho dinâmico automaticamente, chamando a implementação correta para cada tipo concreto.
Exemplo de iteração sobre objetos trait para agregar resultados:
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}
impl Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
}
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 2.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}
Este padrão é útil para agregar resultados — você pode somar valores, filtrar por critérios ou transformar a coleção enquanto interage com os itens puramente através da interface da trait.
Experimente você mesmo
mod items;
use items::{Priceable, Product, Service};
fn main() {
// Ler entradas
let mut product_name = String::new();
std::io::stdin().read_line(&mut product_name).expect("Failed to read line");
let product_name = product_name.trim().to_string();
let mut product_cost = String::new();
std::io::stdin().read_line(&mut product_cost).expect("Failed to read line");
let product_cost: f64 = product_cost.trim().parse().expect("Failed to parse");
let mut service_name = String::new();
std::io::stdin().read_line(&mut service_name).expect("Failed to read line");
let service_name = service_name.trim().to_string();
let mut hourly_rate = String::new();
std::io::stdin().read_line(&mut hourly_rate).expect("Failed to read line");
let hourly_rate: f64 = hourly_rate.trim().parse().expect("Failed to parse");
let mut hours = String::new();
std::io::stdin().read_line(&mut hours).expect("Failed to read line");
let hours: f64 = hours.trim().parse().expect("Failed to parse");
// TODO: Criar um Product usando product_name e product_cost
// TODO: Criar um Service usando service_name, hourly_rate e hours
// TODO: Criar um Vec<Box<dyn Priceable>> e adicionar ambos os itens a ele
// TODO: Iterar pelo vetor e calcular o preço total
// TODO: Imprimir o total com uma casa decimal
// Formato: Total: ${total:.1}
}
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