Desafio Rust
Trait objects e polimorfismo
Enunciado
Defina um trait Forma com métodos area() -> f64 e nome() -> String. Implemente para Circulo e Retangulo. Crie um vetor de Box<dyn Forma> com várias formas, calcule a área total e imprima o nome e a área de cada forma. Depois, crie uma função que recebe &dyn Forma e imprime seus dados.
Requisitos
- Trait
Formacomareaenome. - Implementar para
Circulo(raio) eRetangulo(largura, altura). - Usar
Vec<Box<dyn Forma>>. - Função
imprimir_forma(f: &dyn Forma). - Calcular e imprimir a área total.
Código inicial
trait Forma {
fn area(&self) -> f64;
fn nome(&self) -> String;
}
// implemente Circulo e Retangulo
fn imprimir_forma(f: &dyn Forma) {
// implemente
}
fn main() {
// crie o vetor, chame imprimir_forma e calcule a área total
}
Saída esperada
Círculo: área = 78.54
Retângulo: área = 24.00
Círculo: área = 12.57
Área total: 115.11
Ver dica
Use std::f64::consts::PI para o valor de π. Para formatar com duas casas decimais, use {:.2}.
Mostrar solução
trait Forma {
fn area(&self) -> f64;
fn nome(&self) -> String;
}
struct Circulo {
raio: f64,
}
struct Retangulo {
largura: f64,
altura: f64,
}
impl Forma for Circulo {
fn area(&self) -> f64 {
std::f64::consts::PI * self.raio * self.raio
}
fn nome(&self) -> String {
"Círculo".to_string()
}
}
impl Forma for Retangulo {
fn area(&self) -> f64 {
self.largura * self.altura
}
fn nome(&self) -> String {
"Retângulo".to_string()
}
}
// Função que aceita qualquer tipo que implemente Forma via trait object
fn imprimir_forma(f: &dyn Forma) {
println!("{}: área = {:.2}", f.nome(), f.area());
}
fn main() {
// Vetor de trait objects: cada elemento é um Box<dyn Forma>
let formas: Vec<Box<dyn Forma>> = vec![
Box::new(Circulo { raio: 5.0 }),
Box::new(Retangulo { largura: 4.0, altura: 6.0 }),
Box::new(Circulo { raio: 2.0 }),
];
let mut area_total = 0.0;
for forma in &formas {
imprimir_forma(forma.as_ref()); // &Box<dyn Forma> -> &dyn Forma
area_total += forma.area();
}
println!("Área total: {:.2}", area_total);
}
Passo a passo
- O trait
Formadefine os métodosareaenomeque todas as formas devem implementar. CirculoeRetangulosão structs com os campos necessários.- As implementações de
Formacalculam a área e retornam o nome. imprimir_formarecebe&dyn Forma, permitindo qualquer tipo que implemente o trait sem saber o tipo concreto.- No
main, criamos umVec<Box<dyn Forma>>com diferentes formas. OBoxé necessário porquedyn Formaé um tipo sem tamanho conhecido (unsized). - Iteramos com
for forma in &formas, obtendo&Box<dyn Forma>. Usamosforma.as_ref()para converter para&dyn Formae passar paraimprimir_forma. - A área total é acumulada somando
forma.area().
Por que funciona
Box<dyn Forma> é um ponteiro para um valor no heap que implementa Forma. O dyn indica despacho dinâmico: a chamada de método é resolvida em tempo de execução via vtable. Isso permite armazenar tipos diferentes na mesma coleção. A função imprimir_forma aceita &dyn Forma, promovendo polimorfismo sem genéricos.
Erros comuns
- Esquecer
Boxno vetor:Vec<dyn Forma>não compila porquedyn Formanão tem tamanho conhecido. - Tentar passar
&Box<dyn Forma>diretamente para&dyn Formasemas_ref(): o compilador pode não coagir automaticamente em todos os contextos; useforma.as_ref()ou&**forma. - Não implementar todos os métodos do trait: erro de compilação.
- Usar
f64semstd::f64::consts::PI: não existe constantePIno prelúdio.
Outra forma de resolver
Usar genéricos com impl Trait para despacho estático, mas isso impede armazenar tipos diferentes no mesmo vetor. Para coleções heterogêneas, Box<dyn Trait> é a solução idiomática.
Saída esperada
Círculo: área = 78.54
Retângulo: área = 24.00
Círculo: área = 12.57
Área total: 115.11