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 distintos tipos en un Vec<Box<dyn Trait>>, el siguiente paso es procesar esa collection. Iterar sobre objetos de traits funciona igual que iterar sobre cualquier vector. Recorres cada elemento y llamas a los métodos definidos por el 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());
    }
}

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

Este patrón es potente para agregar resultados. Puedes sumar todas las áreas, filtrar las 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 trait, interactúas con ellos únicamente a través de la interfaz del trait, independientemente de sus tipos subyacentes.

challenge icon

Desafío

Fácil

¡Construyamos una calculadora de precios para un carrito de compras que pueda contener distintos tipos de elementos! Crearás un sistema en el que los productos y los 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 method price que recibe &self y returns un f64. Después, crea dos structs públicos:
    • Product: con campos name (String) y cost (f64) públicos. Su price should simplemente devolver el cost.
    • Service: con campos name (String), hourly_rate (f64) y hours (f64) públicos. Su price should devolver hourly_rate * hours.
  • main.rs: Incluye tu módulo de items y crea un vector de tipo Vec<Box<dyn Priceable>>. Usa las entradas para crear un Product y un Service, add ambos al vector y después recorre la collection para calcular e imprimir el precio total de todos los items.

Aquí es donde brilla el poder de los objetos de traits. Tu código de iteración no necesita saber si está procesando un Product o un Service. Simplemente llama a price() en cada item, y Rust selecciona automáticamente la implementación correcta.

Tu salida should 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 coste 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: Crea un Product usando product_name y product_cost

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

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

    // TODO: Itera a través del vector y calcula el precio total

    // TODO: Imprime 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