Iterando Objetos Trait
Parte da seção Programação Orientada a Objetos 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 os métodos definidos pelo 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());
}
}
Ao iterar com for shape in &shapes, cada shape é uma referência a um Box<dyn Shape>. Você pode chamar diretamente qualquer método do trait Shape: o Rust lida automaticamente com o despacho dinâmico, chamando a implementação correta para cada tipo concreto.
Esse padrão é poderoso para agregar resultados. Você poderia somar todas as áreas, filtrar formas por tamanho ou transformar a coleção. A principal ideia é que, uma vez que os itens estejam em uma coleção de objetos de trait, você interage com eles exclusivamente por meio da interface do trait, independentemente de seus tipos subjacentes.
Desafio
FácilVamos criar uma calculadora de preços para um carrinho de compras que pode conter diferentes tipos de itens! Você criará um sistema no qual produtos e serviços, cada um com uma lógica de preços diferente, possam ser armazenados juntos e processados para calcular um preço total.
Você organizará seu código em dois arquivos:
items.rs: Defina um trait públicoPriceablecom um métodopriceque recebe&selfe retorna umf64. Em seguida, crie duas structs públicas:Product: com os campos públicosname(String) ecost(f64). Seupricedeve simplesmente retornar ocost.Service: com os 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 e, em seguida, itere pela coleção para calcular e imprimir o preço total de todos os itens.
O poder dos objetos de trait se destaca 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 direciona automaticamente para a implementação correta.
Sua saída deve exibir o preço total com duas casas decimais:
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, a tarifa por hora e as horas trabalhadas (analise os valores numéricos como f64).
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: Crie um Product usando product_name e product_cost
// TODO: Crie um Service usando service_name, hourly_rate e hours
// TODO: Crie um Vec<Box<dyn Priceable>> e adicione ambos os itens a ele
// TODO: Itere pelo vetor e calcule o preço total
// TODO: Imprima 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 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