Límites múltiples
Parte de la sección Object Oriented Programming del Journey de Rust de Coddy — lección 41 de 61.
A veces, un solo límite de rasgo (trait bound) no es suficiente. Es posible que necesites un tipo genérico que pueda ser impreso y proporcionar un resumen. Rust te permite requerir múltiples rasgos utilizando la sintaxis +.
Aquí se muestra cómo especificar que un tipo debe implementar dos traits:
use std::fmt::Display;
trait Summary {
fn summarize(&self) -> String;
}
fn announce<T: Display + Summary>(item: T) {
println!("Breaking news: {}", item);
println!("Summary: {}", item.summarize());
}
La restricción T: Display + Summary significa que "T debe implementar tanto Display como Summary". Dentro de la función, puedes usar las capacidades de ambos traits: imprimir con {} (de Display) y llamar a summarize() (de Summary).
Puedes encadenar tantos traits como sea necesario:
fn process<T: Display + Summary + Clone>(item: T) {
// Puede imprimir, resumir Y clonar
}
Este patrón es esencial cuando tu función depende de múltiples comportamientos. En lugar de aceptar cualquier tipo y esperar que funcione, declaras explícitamente qué capacidades se requieren exactamente, y el compilador lo hace cumplir en tiempo de compilación.
Desafío
Fácil¡Vamos a construir un sistema de inspección de productos que requiere que los artículos tengan múltiples capacidades! Crearás una función genérica que solo acepte tipos que implementen tanto un trait personalizado como un trait estándar, demostrando cómo la sintaxis + combina múltiples límites (bounds).
Organizarás tu código en dos archivos:
product.rs: Define un trait públicoInspectablecon un método llamadoinspectque tome&selfy devuelva unStringque contenga los detalles de la inspección. Luego, crea una estructura públicaGadgetcon campos públicosname(String) yserial(u32). Tu Gadget debe implementar tantoInspectable(devolviendo"Inspecting: {}"con el nombre) comostd::fmt::Display(formateando como"{} (SN: {})"con el nombre y el serial). Finalmente, crea una función genérica pública llamadafull_reportque acepte cualquier tipoTque implemente tantoDisplaycomoInspectable. Esta función debe imprimir dos líneas: primero el artículo usando el formateador{}, luego el resultado de llamar ainspect().main.rs: Importa tu módulo product y crea una instancia deGadgetutilizando las entradas proporcionadas. Llama afull_reportcon tu gadget para demostrar que cumple con los requisitos de ambos traits.
El poder de los límites múltiples es que tu función full_report puede usar capacidades de ambos traits —mostrando el artículo de forma agradable Y obteniendo detalles de inspección— todo garantizado en tiempo de compilación.
Tu salida debería mostrar tanto el formato de visualización como el resultado de la inspección:
{name} (SN: {serial})
Inspecting: {name}Por ejemplo, con las entradas Smartwatch y 98765:
Smartwatch (SN: 98765)
Inspecting: SmartwatchRecibirás dos entradas: el nombre del gadget y el número de serie (analízalo como u32).
Hoja de referencia
Puedes requerir múltiples límites de trait (trait bounds) en un tipo genérico utilizando la sintaxis +:
fn function_name<T: Trait1 + Trait2>(item: T) {
// Puede usar capacidades de ambos traits
}
Ejemplo con Display y un trait personalizado:
use std::fmt::Display;
trait Summary {
fn summarize(&self) -> String;
}
fn announce<T: Display + Summary>(item: T) {
println!("Breaking news: {}", item);
println!("Summary: {}", item.summarize());
}
Puedes encadenar múltiples traits según sea necesario:
fn process<T: Display + Summary + Clone>(item: T) {
// Puede imprimir, resumir Y clonar
}
El compilador obliga a que el tipo implemente todos los traits especificados en tiempo de compilación.
Pruébalo tú mismo
mod product;
use product::{Gadget, full_report};
fn main() {
// Leer entradas
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Failed to read line");
let name = name.trim().to_string();
let mut serial_input = String::new();
std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
// TODO: Crear una instancia de Gadget con el nombre y el serial
// TODO: Llamar a full_report con tu gadget
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
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 multimedia