La cláusula where
Parte de la sección Programación Orientada a Objetos del Journey de Rust de Coddy. Lección 42 de 61.
A medida que añades más restricciones de traits, las firmas de las funciones pueden volverse difíciles de leer. Considera una función con varios 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á volviendo demasiado cargada. 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 se refiere únicamente a la legibilidad. Los requisitos de cada tipo aparecen en su propia línea, lo que facilita revisar lo que necesita cada parámetro genérico.
La cláusula where resulta especialmente valiosa cuando las restricciones son complejas o cuando tienes muchos parámetros genéricos. Mantiene el nombre y los parámetros de la función 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 las restricciones van más allá de un simple T: Trait.
Desafío
Fácil¡Construyamos un sistema de procesamiento de datos que use la cláusula where para mantener legibles los límites de traits complejos! Crearás una función genérica con varios parámetros de tipo, cada uno de los cuales requiere distintos traits, y organizarás los límites claramente mediante la sintaxis where.
Organizarás tu código en dos archivos:
processor.rs: Define dos traits públicos y una función genérica que use ambos:Summarize: un trait con un métodosummaryque toma&selfy devuelve unStringValidate: un trait con un métodovalidateque toma&selfy devuelve unbool
process_itemsque acepte dos parámetros de tipos genéricos diferentes. Usa una cláusulawherepara especificar que el primer tipo debe implementar tantoClonecomoSummarize, mientras que el segundo tipo debe implementarValidate. La función debe imprimir el resumen del primer elemento y después imprimir si el segundo elemento es válido.main.rs: Crea dos structs públicos que implementen los traits requeridos:Article: con un campo públicotitle(String). ImplementaSummarizepara devolverArticle: {title}y derivaClone.Form: con un campo públicofilled(bool). ImplementaValidatepara devolver el valor defilled.
process_itemscon ambos.
La cláusula where hace que la firma de tu función sea mucho más limpia que si incluyeras todos los límites entre los corchetes 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: trueY con las entradas Breaking News y false:
Article: Breaking News
Valid: falseRecibirás dos entradas: el título del artículo y si el formulario está rellenado (parsea como bool).
Pruébalo tú mismo
mod processor;
use processor::{Summarize, Validate, process_items};
use std::io;
// TODO: Define una struct pública Article con un campo público title (String)
// Deriva Clone para Article
// TODO: Implementa Summarize para Article
// El método summary debe devolver "Article: {title}"
// TODO: Define una struct pública Form con un campo público filled (bool)
// TODO: Implementa 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: Crea un Article con el title dado
// TODO: Crea un Form con el valor filled dado
// TODO: Llama a process_items con el article y form
}
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