Menu
Coddy logo textTech

Iteración de objetos de Trait

Parte de la sección Programación Orientada a Objetos 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 colección. Iterar sobre objetos de rasgo 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 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 muy potente para agregar resultados. Podrías 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 rasgo, interactúas con ellos únicamente a través de la interfaz del rasgo, 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 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 recibe &self y devuelve un f64. Después, crea dos structs públicos:
    • Product: con campos públicos name (String) y cost (f64). Su price debe devolver simplemente el cost.
    • Service: con campos públicos name (String), hourly_rate (f64) y hours (f64). Su price debe devolver hourly_rate * hours.
  • main.rs: Incorpora 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 y, después, itera por la colección para calcular e imprimir el precio total de todos los artículos.

Aquí es donde se aprecia 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 artículo, y Rust selecciona automáticamente la implementación correcta.

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: 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 Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Rust online