Menu
Coddy logo textTech

Sintaxis de límites de traits

Parte de la sección Object Oriented Programming del Journey de Rust de Coddy — lección 40 de 61.

Hasta ahora, has aprendido a definir traits e implementarlos para structs. También has trabajado con genéricos para escribir código flexible. Ahora es el momento de combinar estos conceptos: usar traits para restringir qué tipos puede aceptar una función genérica.

Considera una función genérica que necesita llamar a un método específico en su parámetro. Sin ninguna restricción, Rust no tiene garantía de que el tipo T tenga ese método:

fn print_info<T>(item: T) {
    println!("{}", item.summarize());  // ¡Error! Es posible que T no tenga summarize()
}

La solución es una restricción de trait (trait bound). Al añadir : TraitName después del parámetro genérico, le indicas a Rust que T debe implementar ese trait:

trait Summary {
    fn summarize(&self) -> String;
}

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());  // Ahora Rust sabe que este método existe
}

La sintaxis <T: Summary> se lee como "T es cualquier tipo que implementa Summary". Ahora la función solo acepta tipos que tienen el comportamiento requerido. Si intentas pasar un tipo que no implementa Summary, el compilador lo rechazará con un error claro.

Este patrón es potente porque te permite escribir código genérico que sigue teniendo acceso a una funcionalidad específica. Tu función sigue siendo flexible (funciona con cualquier tipo), pero solo con tipos que proporcionan el comportamiento que necesitas.

challenge icon

Desafío

Fácil

¡Vamos a construir un sistema de notificaciones que utiliza límites de rasgos (trait bounds) para asegurar que solo se puedan enviar mensajes con el formato adecuado! Crearás una función genérica que acepte cualquier tipo que implemente un rasgo específico, demostrando cómo los límites de rasgos restringen los parámetros genéricos.

Organizarás tu código en dos archivos:

  • notification.rs: Define un rasgo público Notify con un método llamado message que tome &self y devuelva un String. Luego, crea dos estructuras públicas que implementen este rasgo:
    • Email — con un campo público subject (String). Su método message debe devolver Email: {subject}
    • SMS — con un campo público content (String). Su método message debe devolver SMS: {content}
    Finalmente, crea una función genérica pública llamada send_notification que acepte cualquier tipo T que implemente el rasgo Notify. Esta función debe imprimir el resultado de llamar a message() en el elemento.
  • main.rs: Incorpora tu módulo de notificación y utiliza las entradas proporcionadas para crear una instancia de Email y otra de SMS. Llama a send_notification con cada una para demostrar que tu función genérica funciona con cualquier tipo que implemente Notify.

La idea clave aquí es que send_notification no conoce el tipo concreto que recibe; solo sabe que el tipo puede producir un mensaje. El límite de rasgo T: Notify garantiza esta capacidad.

Tu salida debería mostrar el envío de ambas notificaciones:

Email: {subject}
SMS: {content}

Por ejemplo, con las entradas Meeting Tomorrow y On my way!:

Email: Meeting Tomorrow
SMS: On my way!

Recibirás dos entradas: el asunto del correo electrónico (email subject) y el contenido del SMS.

Hoja de referencia

Un trait bound restringe un parámetro de tipo genérico para que solo acepte tipos que implementen un trait específico.

La sintaxis para los trait bounds utiliza : TraitName después del parámetro genérico:

fn function_name<T: TraitName>(item: T) {
    // Ahora se pueden llamar métodos de TraitName en item
}

Ejemplo con un trait bound:

trait Summary {
    fn summarize(&self) -> String;
}

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());
}

La notación <T: Summary> significa "T es cualquier tipo que implemente Summary". Esto permite que la función llame a los métodos del trait mientras sigue siendo genérica.

Pruébalo tú mismo

mod notification;

use notification::{Email, SMS, send_notification};

fn main() {
    // Leer entradas
    let mut subject = String::new();
    std::io::stdin().read_line(&mut subject).expect("Failed to read line");
    let subject = subject.trim().to_string();
    
    let mut content = String::new();
    std::io::stdin().read_line(&mut content).expect("Failed to read line");
    let content = content.trim().to_string();
    
    // TODO: Crear una instancia de Email con el asunto (subject)
    
    // TODO: Crear una instancia de SMS con el contenido (content)
    
    // TODO: Llamar a send_notification con el email
    
    // TODO: Llamar a send_notification con el sms
}
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