Menu
Coddy logo textTech

La cláusula where

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

A medida que agregas más restricciones de trait, las firmas de las funciones pueden volverse difíciles de leer. Considera una función con múltiples parámetros genéricos, cada uno de los cuales requiere varios traits:

fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
    // ...
}

Esto funciona, pero la firma se está saturando. La cláusula where ofrece una alternativa más limpia al mover los límites de los traits después de la lista de parámetros:

fn complex_operation<T, U>(first: T, second: U)
where
    T: Display + Clone,
    U: Debug + Summary,
{
    // ...
}

Ambas versiones son funcionalmente idénticas; la cláusula where trata puramente sobre la legibilidad. Los requisitos de cada tipo aparecen en su propia línea, lo que facilita ver rápidamente lo que cada parámetro genérico necesita.

La cláusula where se vuelve especialmente valiosa cuando los límites son complejos o cuando tienes muchos parámetros genéricos. Mantiene el nombre de la función y los parámetros visibles de un vistazo, con las restricciones enumeradas por separado a continuación. Este es el estilo preferido en la mayoría de las bases de código de Rust cuando los límites se extienden más allá de un simple T: Trait.

challenge icon

Desafío

Fácil

¡Vamos a construir un sistema de procesamiento de datos que utiliza la cláusula where para mantener legibles los límites de rasgos (trait bounds) complejos! Crearás una función genérica con múltiples parámetros de tipo, cada uno de los cuales requiere diferentes rasgos, y organizarás los límites de forma limpia utilizando la sintaxis where.

Organizarás tu código en dos archivos:

  • processor.rs: Define dos rasgos públicos y una función genérica que los utilice a ambos:
    • Summarize — un rasgo con un método summary que toma &self y devuelve un String
    • Validate — un rasgo con un método validate que toma &self y devuelve un bool
    Luego, crea una función genérica pública llamada process_items que acepte dos parámetros de diferentes tipos genéricos. Usa una cláusula where para especificar que el primer tipo debe implementar tanto Clone como Summarize, mientras que el segundo tipo debe implementar Validate. La función debe imprimir el resumen del primer elemento y luego imprimir si el segundo elemento es válido.
  • main.rs: Crea dos estructuras públicas que implementen los rasgos requeridos:
    • Article — con un campo público title (String). Implementa Summarize para que devuelva "Article: {}" con el título, y deriva Clone.
    • Form — con un campo público filled (bool). Implementa Validate para que devuelva el valor de filled.
    Usa las entradas proporcionadas para crear un Article y un Form, luego llama a process_items con ambos.

La cláusula where hace que la firma de tu función sea mucho más limpia que amontonar todos los límites dentro de los paréntesis angulares. Los requisitos de cada tipo aparecen en su propia línea, lo que facilita ver qué necesita cada parámetro genérico.

Tu salida debe mostrar el resumen seguido del resultado de la validación:

Article: {title}
Valid: {true/false}

Por ejemplo, con las entradas Rust Tips y true:

Article: Rust Tips
Valid: true

Y con las entradas Breaking News y false:

Article: Breaking News
Valid: false

Recibirás dos entradas: el título del artículo y si el formulario está completo (analízalo como bool).

Hoja de referencia

La cláusula where proporciona una forma más limpia de especificar límites de rasgos (trait bounds) para funciones genéricas al mover las restricciones después de la lista de parámetros:

fn complex_operation<T, U>(first: T, second: U)
where
    T: Display + Clone,
    U: Debug + Summary,
{
    // ...
}

Esto es funcionalmente idéntico a los límites de rasgos en línea, pero mejora la legibilidad:

fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
    // ...
}

La cláusula where es especialmente útil cuando:

  • Tienes múltiples parámetros genéricos
  • Cada parámetro requiere varios límites de rasgos
  • Quieres mantener la firma de la función limpia y fácil de leer

Los requisitos de cada tipo aparecen en su propia línea, lo que facilita ver qué necesita cada parámetro genérico. Este es el estilo preferido en la mayoría de las bases de código de Rust cuando los límites se extienden más allá de un simple T: Trait.

Pruébalo tú mismo

mod processor;

use processor::{Summarize, Validate, process_items};
use std::io;

// TODO: Definir una estructura pública Article con un campo público title (String)
// Derivar Clone para Article

// TODO: Implementar Summarize para Article
// El método summary debe devolver "Article: {title}"

// TODO: Definir una estructura pública Form con un campo público filled (bool)

// TODO: Implementar Validate para Form
// El método validate debe devolver el valor de filled

fn main() {
    let mut title = String::new();
    io::stdin().read_line(&mut title).expect("Failed to read line");
    let title = title.trim().to_string();

    let mut filled_input = String::new();
    io::stdin().read_line(&mut filled_input).expect("Failed to read line");
    let filled: bool = filled_input.trim().parse().expect("Failed to parse bool");

    // TODO: Crear un Article con el título dado
    
    // TODO: Crear un Form con el valor filled dado
    
    // TODO: Llamar a process_items con el artículo y el formulario
}
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