Menu
Coddy logo textTech

Iteración de objetos de Trait

Parte de la sección Object Oriented Programming del Journey de Rust de Coddy. Lección 47 de 61.

Ahora que puedes almacenar diferentes tipos en un Vec<Box<dyn Trait>>, el siguiente paso es procesar esa colección. Iterar sobre objetos de rasgo (trait objects) funciona igual que iterar sobre cualquier vector: recorres cada elemento y llamas a los métodos definidos por el rasgo.

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());
    }
}

Cuando iteras con for shape in &shapes, cada shape es una referencia a un Box<dyn Shape>. Puedes llamar a cualquier método del trait Shape directamente—Rust maneja el despacho dinámico automáticamente, llamando a la implementación correcta para cada tipo concreto.

Este patrón es potente para agregar resultados. Podrías sumar todas las áreas, filtrar formas por tamaño o transformar la colección. La idea clave es que una vez que los elementos están en una colección de objetos de rasgo (trait objects), interactúas con ellos puramente a través de la interfaz del rasgo, independientemente de sus tipos subyacentes.

challenge icon

Desafío

Fácil

¡Vamos a construir una calculadora de precios para un carrito de compras que puede contener diferentes tipos de artículos! Crearás un sistema donde los productos y servicios —cada uno con una lógica de precios diferente— puedan almacenarse juntos y procesarse para calcular un precio total.

Organizarás tu código en dos archivos:

  • items.rs: Define un trait público Priceable con un método price que tome &self y devuelva un f64. Luego crea dos structs públicos:
    • Product — con campos públicos name (String) y cost (f64). Su price simplemente debe devolver el cost.
    • Service — con campos públicos name (String), hourly_rate (f64), y hours (f64). Su price debe devolver hourly_rate * hours.
  • main.rs: Importa tu módulo de artículos y crea un vector de tipo Vec<Box<dyn Priceable>>. Usa las entradas para crear un Product y un Service, añádelos a tu vector, luego itera a través de la colección para calcular e imprimir el precio total de todos los artículos.

El poder de los objetos de trait brilla aquí: tu código de iteración no necesita saber si está procesando un Product o un Service. Simplemente llama a price() en cada artículo, y Rust despacha la implementación correcta automáticamente.

Tu salida debe mostrar el precio total con dos decimales:

Total: ${total}

Por ejemplo, con las entradas Laptop, 999.99, Consulting, 150.0, y 3.0:

Total: $1449.99

Y con las entradas Book, 29.99, Tutoring, 50.0, y 2.0:

Total: $129.99

Recibirás cinco entradas: el nombre del producto, el costo del producto, el nombre del servicio, la tarifa por hora y las horas trabajadas (analiza los valores numéricos como f64).

Pruébalo tú mismo

mod items;

use items::{Priceable, Product, Service};

fn main() {
    // Leer 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: Crear un Product usando product_name y product_cost

    // TODO: Crear un Service usando service_name, hourly_rate y hours

    // TODO: Crear un Vec<Box<dyn Priceable>> y añadir ambos elementos a él

    // TODO: Iterar a través del vector y calcular el precio total

    // TODO: Imprimir el total con un decimal
    // Formato: Total: ${total:.1}
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming

Practica por tu cuenta: Compilador de Rust online