A Cláusula Where
Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 42 de 61.
À medida que você adiciona mais restrições de trait (trait bounds), as assinaturas de função podem se tornar difíceis de ler. Considere uma função com múltiplos parâmetros genéricos, cada um exigindo várias traits:
fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
// ...
}
Isso funciona, mas a assinatura está ficando lotada. A cláusula where oferece uma alternativa mais limpa ao mover os trait bounds para depois da lista de parâmetros:
fn complex_operation<T, U>(first: T, second: U)
where
T: Display + Clone,
U: Debug + Summary,
{
// ...
}
Ambas as versões são funcionalmente idênticas — a cláusula where serve puramente para legibilidade. Os requisitos de cada tipo aparecem em sua própria linha, tornando fácil visualizar o que cada parâmetro genérico precisa.
A cláusula where torna-se especialmente valiosa quando os limites são complexos ou quando você tem muitos parâmetros genéricos. Ela mantém o nome da função e os parâmetros visíveis à primeira vista, com as restrições listadas separadamente abaixo. Este é o estilo preferido na maioria das bases de código Rust quando os limites se estendem além de um simples T: Trait.
Desafio
FácilVamos construir um sistema de processamento de dados que utiliza a cláusula where para manter limites de traits (trait bounds) complexos legíveis! Você criará uma função genérica com múltiplos parâmetros de tipo, cada um exigindo traits diferentes, e organizará os limites de forma limpa usando a sintaxe where.
Você organizará seu código em dois arquivos:
processor.rs: Defina duas traits públicas e uma função genérica que utilize ambas:Summarize— uma trait com um métodosummaryque recebe&selfe retorna umaStringValidate— uma trait com um métodovalidateque recebe&selfe retorna umbool
process_itemsque aceite dois parâmetros de tipos genéricos diferentes. Use uma cláusulawherepara especificar que o primeiro tipo deve implementar tantoClonequantoSummarize, enquanto o segundo tipo deve implementarValidate. A função deve imprimir o resumo do primeiro item e, em seguida, imprimir se o segundo item é válido.main.rs: Crie duas structs públicas que implementem as traits necessárias:Article— com um campo públicotitle(String). ImplementeSummarizepara retornar"Article: {}"com o título, e deriveClone.Form— com um campo públicofilled(bool). ImplementeValidatepara retornar o valor defilled.
process_itemscom ambos.
A cláusula where torna a assinatura da sua função muito mais limpa do que amontoar todos os limites dentro dos colchetes angulares. Os requisitos de cada tipo aparecem em sua própria linha, facilitando a visualização do que cada parâmetro genérico precisa.
Sua saída deve mostrar o resumo seguido pelo resultado da validação:
Article: {title}
Valid: {true/false}Por exemplo, com as entradas Rust Tips e true:
Article: Rust Tips
Valid: trueE com as entradas Breaking News e false:
Article: Breaking News
Valid: falseVocê receberá duas entradas: o título do artigo e se o formulário está preenchido (analise como bool).
Folha de consulta
A cláusula where fornece uma maneira mais limpa de especificar limites de trait para funções genéricas, movendo as restrições para depois da lista de parâmetros:
fn complex_operation<T, U>(first: T, second: U)
where
T: Display + Clone,
U: Debug + Summary,
{
// ...
}
Isso é funcionalmente idêntico aos limites de trait inline, mas melhora a legibilidade:
fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
// ...
}
A cláusula where é especialmente útil quando:
- Você tem múltiplos parâmetros genéricos
- Cada parâmetro requer vários limites de trait
- Você deseja manter a assinatura da função limpa e fácil de escanear
Os requisitos de cada tipo aparecem em sua própria linha, facilitando a visualização do que cada parâmetro genérico precisa. Este é o estilo preferido na maioria das bases de código Rust quando os limites se estendem além de um simples T: Trait.
Experimente você mesmo
mod processor;
use processor::{Summarize, Validate, process_items};
use std::io;
// TODO: Defina uma struct Article pública com um campo title público (String)
// Derive Clone para Article
// TODO: Implemente Summarize para Article
// O método summary deve retornar "Article: {title}"
// TODO: Defina uma struct Form pública com um campo filled público (bool)
// TODO: Implemente Validate para Form
// O método validate deve retornar o 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: Crie um Article com o title fornecido
// TODO: Crie um Form com o valor filled fornecido
// TODO: Chame process_items com o article e form
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Métodos e Comportamento
Introdução a Blocos de ImplementaçãoO Parâmetro SelfMétodos MutáveisFunções AssociadasMúltiplos Blocos de ImplementaçãoEncadeamento de MétodosRecapitulação - Ações de Retângulo4Projeto: Pet Virtual
Definindo o PetAlimentando o Pet7Traits Padrão
A Trait DebugA Trait DisplayClone e CopyTraits de IgualdadeRecapitulação - Ponto Imprimível10Projeto: Sistema de Documentos
A Trait DrawComponente de Texto2Encapsulamento e Módulos
Fundamentos de MódulosA Palavra-chave PublicCampos PrivadosGettersSettersRecapitulação - Cofre Seguro5Genéricos
Structs GenéricasMétodos GenéricosMúltiplos Tipos GenéricosFunções GenéricasRecapitulação - Ponto de Coordenada8Traits como Restrições
Sintaxe de Restrições de TraitRestrições MúltiplasA Cláusula WhereRetornando Tipos com TraitsRecapitulação - Impressora Genérica11Padrões de Projeto em Rust
Padrão NewtypeComposiçãoA Trait DropFrom e IntoRecapitulação - Mock de Smart Pointer3Enums Avançados
Enums com DadosMétodos em EnumsCorrespondência de Variantes de DadosO Enum Option RevisitadoRecapitulação - Enum Shape6Definição de Traits
O que é uma Trait?Implementando TraitsImplementações PadrãoSobrescrevendo PadrõesTraits com ParâmetrosRecapitulação - Media Player