Menu
Coddy logo textTech

Vectores de Traits

Parte de la sección Programación Orientada a Objetos del Journey de Rust de Coddy. Lección 46 de 61.

Has visto cómo Box<dyn Trait> permite que una sola variable contenga diferentes tipos. La verdadera potencia surge cuando combinas esto con colecciones: almacenar varios tipos diferentes en un solo vector.

Un vector normal requiere que todos los elementos sean del mismo tipo. No puedes tener un Vec<Dog> que también contenga gatos. Pero con objetos de rasgo, puedes crear un vector que contenga cualquier cosa que implemente un rasgo compartido:

trait Speak {
    fn speak(&self) -> String;
}

struct Dog;
struct Cat;
struct Bird;

impl Speak for Dog {
    fn speak(&self) -> String { String::from("Woof!") }
}

impl Speak for Cat {
    fn speak(&self) -> String { String::from("Meow!") }
}

impl Speak for Bird {
    fn speak(&self) -> String { String::from("Tweet!") }
}

fn main() {
    let animals: Vec<Box<dyn Speak>> = vec![
        Box::new(Dog),
        Box::new(Cat),
        Box::new(Bird),
    ];
}

El tipo Vec<Box<dyn Speak>> significa «un vector de objetos de rasgo encapsulados». Cada elemento es un Box que apunta a algo que implementa Speak: los tipos reales pueden ser diferentes. Envuelves cada valor con Box::new() al añadirlo al vector.

Este patrón es esencial para crear sistemas flexibles en los que necesitas gestionar colecciones de objetos relacionados pero diferentes, como componentes de UI, entidades de juegos o sistemas de plugins.

challenge icon

Desafío

Fácil

¡Construyamos un sistema de notificaciones que pueda gestionar distintos tipos de alertas! Crearás una colección que almacena varios tipos de notificaciones, correos electrónicos, mensajes SMS y notificaciones push, todo en un único vector mediante objetos de trait.

Organizarás tu código en dos archivos:

  • notifications.rs: Define un trait público Notify con un método send que recibe &self y devuelve un String que describe la notificación que se está enviando. Después, crea tres structs públicos:
    • Email: con un campo público recipient (String). Su método send debe devolver Email to: {recipient}
    • Sms: con un campo público phone (String). Su método send debe devolver SMS to: {phone}
    • Push: con un campo público device (String). Su método send debe devolver Push to: {device}
  • main.rs: Incorpora tu módulo de notificaciones y crea un vector de tipo Vec<Box<dyn Notify>> que contenga los tres tipos de notificación. Usa las entradas proporcionadas para crear una notificación de cada tipo, añádelas a tu vector en orden (Email, Sms, Push) y después imprime el resultado de llamar a send() en el primer elemento del vector.

El concepto clave aquí es que, aunque Email, Sms y Push sean structs completamente diferentes, todos pueden convivir en el mismo vector porque comparten el trait Notify. Cada elemento se envuelve con Box::new() para crear el objeto de trait.

Tu salida debe mostrar la primera notificación:

Email to: {recipient}

Por ejemplo, con las entradas alice@example.com, 555-1234 y iPhone-12:

Email to: alice@example.com

Recibirás tres entradas: el destinatario del correo electrónico, el número de teléfono y el nombre del dispositivo.

Pruébalo tú mismo

mod notifications;

use notifications::{Notify, Email, Sms, Push};

fn main() {
    // Leer entradas
    let mut recipient = String::new();
    std::io::stdin().read_line(&mut recipient).expect("Failed to read line");
    let recipient = recipient.trim().to_string();

    let mut phone = String::new();
    std::io::stdin().read_line(&mut phone).expect("Failed to read line");
    let phone = phone.trim().to_string();

    let mut device = String::new();
    std::io::stdin().read_line(&mut device).expect("Failed to read line");
    let device = device.trim().to_string();

    // TODO: Crea un Vec<Box<dyn Notify>> para contener diferentes tipos de notificación
    // TODO: Crea instancias de Email, Sms y Push usando las entradas
    // TODO: Añádelos al vector en orden (Email, Sms, Push) usando Box::new()
    // TODO: Imprime el resultado de llamar a send() en el primer elemento
}
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