Menu
Coddy logo textTech

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.

challenge icon

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úblico Inspectable con un método llamado inspect que tome &self y devuelva un String que contenga los detalles de la inspección. Luego, crea una estructura pública Gadget con campos públicos name (String) y serial (u32). Tu Gadget debe implementar tanto Inspectable (devolviendo "Inspecting: {}" con el nombre) como std::fmt::Display (formateando como "{} (SN: {})" con el nombre y el serial). Finalmente, crea una función genérica pública llamada full_report que acepte cualquier tipo T que implemente tanto Display como Inspectable. Esta función debe imprimir dos líneas: primero el artículo usando el formateador {}, luego el resultado de llamar a inspect().
  • main.rs: Importa tu módulo product y crea una instancia de Gadget utilizando las entradas proporcionadas. Llama a full_report con 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: Smartwatch

Recibirá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
}
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