Sintaxis de límites de traits
Parte de la sección Programación Orientada a Objetos 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 de su parámetro. Sin ninguna restricción, Rust no puede garantizar que el tipo T tenga ese método:
fn print_info<T>(item: T) {
println!("{}", item.summarize()); // ¡Error! T podría no tener summarize()
}
La solución es un límite de trait. 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 aún tiene acceso a una funcionalidad específica. Tu función sigue siendo flexible y funciona con cualquier tipo, pero solo con los tipos que proporcionan el comportamiento que necesitas.
Desafío
Fácil¡Construyamos un sistema de notificaciones que use límites de traits para garantizar que solo se puedan enviar mensajes con el formato adecuado! Crearás una función genérica que acepte cualquier tipo que implemente un trait específico, demostrando cómo los límites de traits restringen los parámetros genéricos.
Organizarás tu código en dos archivos:
notification.rs: Define un trait públicoNotifycon un método llamadomessageque recibe&selfy devuelve unString. Después, crea dos structs públicos que implementen este trait:Email: con un campo públicosubject(String). Su métodomessagedebe devolverEmail: {subject}SMS: con un campo públicocontent(String). Su métodomessagedebe devolverSMS: {content}
send_notificationque acepte cualquier tipoTque implemente el traitNotify. Esta función debe imprimir el resultado de llamar amessage()sobre el elemento.main.rs: Incluye tu módulo de notificaciones y usa las entradas proporcionadas para crear una instancia deEmaily una deSMS. Llama asend_notificationcon cada una para demostrar que tu función genérica funciona con cualquier tipo que implementeNotify.
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 del trait T: Notify garantiza esta capacidad.
Tu salida debe mostrar que ambas notificaciones se han enviado:
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 y el contenido del SMS.
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: Crea una instancia de Email con el subject
// TODO: Crea una instancia de SMS con el content
// TODO: Llama a send_notification con el email
// TODO: Llama a send_notification con el sms
}
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
1Métodos y Comportamiento
Intro a Bloques de ImplementaciónEl Parámetro SelfMétodos MutablesFunciones AsociadasMúltiples Bloques de ImplementaciónEncadenamiento de MétodosResumen - Acciones de Rectángulo4Proyecto: Mascota virtual
Definir la mascotaAlimentar a la mascota7Traits estándar
El trait DebugEl trait DisplayClone y CopyTraits de igualdadResumen - Punto imprimible10Proyecto: Sistema de documentos
El Trait DrawComponente de texto2Encapsulamiento y módulos
Conceptos básicos de módulosLa palabra clave PublicCampos privadosGettersSettersRepaso - Casillero seguro5Genéricos
Estructuras genéricasMétodos genéricosMúltiples tipos genéricosFunciones genéricasResumen - Punto de coordenadas8Traits como límites
Sintaxis de límites de traitsLímites múltiplesLa cláusula whereRetornar tipos con traitsResumen: Impresora genérica11Patrones de diseño en Rust
Patrón NewtypeComposiciónEl Trait DropFrom e IntoResumen - Mock de Smart Pointer3Enums avanzados
Enums con datosMétodos en EnumsCoincidencia de variantes de datosEl Enum Option revisitadoResumen - Enum Shape6Definición de Traits
¿Qué es un Trait?Implementación de TraitsImplementaciones por defectoSobrescribir valores por defectoTraits con parámetrosResumen - Reproductor multimediaPractica por tu cuenta: Compilador de Rust online