Sintaxe de Restrições de Trait
Parte da seção Programação Orientada a Objetos do Journey de Rust da Coddy. Lição 40 de 61.
Até agora, você aprendeu a definir traits e implementá-los para structs. Você também trabalhou com genéricos para escrever código flexível. Agora é hora de combinar esses conceitos: usar traits para restringir quais tipos uma função genérica pode aceitar.
Considere uma função genérica que precisa chamar um método específico em seu parâmetro. Sem nenhuma restrição, Rust não garante que o tipo T tenha esse método:
fn print_info<T>(item: T) {
println!("{}", item.summarize()); // Erro! T pode não ter summarize()
}
A solução é um limite de trait. Ao adicionar : TraitName após o parâmetro genérico, você informa ao Rust que T deve implementar essa trait:
trait Summary {
fn summarize(&self) -> String;
}
fn print_info<T: Summary>(item: T) {
println!("{}", item.summarize()); // Agora o Rust sabe que este método existe
}
A sintaxe <T: Summary> pode ser lida como “T é qualquer tipo que implementa Summary.” Agora, a função aceita apenas tipos que têm o comportamento necessário. Se você tentar passar um tipo que não implementa Summary, o compilador o rejeitará com um erro claro.
Esse padrão é poderoso porque permite escrever código genérico que ainda tem acesso a funcionalidades específicas. Sua função permanece flexível: ela funciona com qualquer tipo, mas apenas com tipos que fornecem o comportamento de que você precisa.
Desafio
FácilVamos criar um sistema de notificações que usa limites de traits para garantir que somente mensagens formatadas corretamente possam ser enviadas! Você criará uma função genérica que aceita qualquer tipo que implemente uma trait específica, demonstrando como os limites de traits restringem parâmetros genéricos.
Você organizará seu código em dois arquivos:
notification.rs: Defina uma trait públicaNotifycom um método chamadomessageque recebe&selfe retorna umaString. Em seguida, crie duas structs públicas que implementem essa trait:Email: com um campo públicosubject(String). Seu métodomessagedeve retornarEmail: {subject}SMS: com um campo públicocontent(String). Seu métodomessagedeve retornarSMS: {content}
send_notificationque aceite qualquer tipoTque implemente a traitNotify. Essa função deve imprimir o resultado da chamada demessage()no item.main.rs: Inclua seu módulo de notificações e use as entradas fornecidas para criar uma instância deEmaile uma deSMS. Chamesend_notificationcom cada uma para demonstrar que sua função genérica funciona com qualquer tipo que implementeNotify.
A principal ideia aqui é que send_notification não sabe qual é o tipo concreto recebido. Ela sabe apenas que o tipo pode produzir uma mensagem. O limite de trait T: Notify garante essa capacidade.
Sua saída deve mostrar as duas notificações sendo enviadas:
Email: {subject}
SMS: {content}Por exemplo, com as entradas Meeting Tomorrow e On my way!:
Email: Meeting Tomorrow
SMS: On my way!Você receberá duas entradas: o assunto do e-mail e o conteúdo do SMS.
Experimente você mesmo
mod notification;
use notification::{Email, SMS, send_notification};
fn main() {
// Ler entradas
let mut subject = String::new();
std::io::stdin().read_line(&mut subject).expect("Failed to read line");
let subject = subject.trim().to_string();
let mut content = String::new();
std::io::stdin().read_line(&mut content).expect("Failed to read line");
let content = content.trim().to_string();
// TODO: Crie uma instância de Email com o subject
// TODO: Crie uma instância de SMS com o content
// TODO: Chame send_notification com o email
// TODO: Chame send_notification com o sms
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
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 PlayerPratique por conta própria: Compilador de Rust online