Menu
Coddy logo textTech

Vectores de Traits

Parte de la sección Object Oriented Programming 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. El verdadero poder surge cuando combinas esto con colecciones: almacenando múltiples tipos diferentes en un solo vector.

Un vector regular requiere que todos los elementos sean del mismo tipo. No puedes tener un Vec<Dog> que también contenga gatos. Pero con los objetos de rasgo (trait objects), 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 (trait objects) en cajas (boxed)". Cada elemento es un Box que apunta a algo que implementa Speak—los tipos reales pueden diferir. Envuelves cada valor con Box::new() al añadirlo al vector.

Este patrón es esencial para construir sistemas flexibles donde necesites gestionar colecciones de objetos relacionados pero diferentes, como componentes de interfaz de usuario (UI), entidades de juego o sistemas de complementos (plugins).

challenge icon

Desafío

Fácil

¡Vamos a construir un sistema de notificaciones que pueda manejar diferentes tipos de alertas! Crearás una colección que almacene varios tipos de notificaciones—correos electrónicos, mensajes SMS y notificaciones push—todo en un único vector utilizando objetos de rasgo (trait objects).

Organizarás tu código en dos archivos:

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

El concepto clave aquí es que, a pesar de que Email, Sms y Push son 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 rasgo.

Tu salida debería 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: Crear un Vec<Box<dyn Notify>> para contener diferentes tipos de notificación
    // TODO: Crear instancias de Email, Sms y Push usando las entradas
    // TODO: Añadirlas al vector en orden (Email, Sms, Push) usando Box::new()
    // TODO: Imprimir 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 Object Oriented Programming