Menu
Coddy logo textTech

Sintaxe de Restrições de Trait

Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 40 de 61.

Até agora, você aprendeu a definir traits e implementá-las para structs. Você também trabalhou com genéricos para escrever código flexível. Agora é hora de combinar esses conceitos — usando 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 quaisquer restrições, o Rust não tem garantia de que o tipo T possui esse método:

fn print_info<T>(item: T) {
    println!("{}", item.summarize());  // Erro! T pode não ter summarize()
}

A solução é um vínculo de trait (trait bound). Ao adicionar : TraitName após o parâmetro genérico, você diz 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> lê-se como "T é qualquer tipo que implementa Summary." Agora a função aceita apenas tipos que possuem o comportamento exigido. Se você tentar passar um tipo que não implementa Summary, o compilador o rejeitará com um erro claro.

Este padrão é poderoso porque permite que você escreva 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 tipos que fornecem o comportamento que você precisa.

challenge icon

Desafio

Fácil

Vamos construir um sistema de notificação que usa trait bounds para garantir que apenas 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 as trait bounds restringem os parâmetros genéricos.

Você organizará seu código em dois arquivos:

  • notification.rs: Defina uma trait pública Notify com um método chamado message que recebe &self e retorna uma String. Em seguida, crie duas structs públicas que implementem essa trait:
    • Email — com um campo público subject (String). Seu método message deve retornar Email: {subject}
    • SMS — com um campo público content (String). Seu método message deve retornar SMS: {content}
    Finalmente, crie uma função genérica pública chamada send_notification que aceita qualquer tipo T que implemente a trait Notify. Esta função deve imprimir o resultado da chamada de message() no item.
  • main.rs: Importe seu módulo de notificação e use as entradas fornecidas para criar uma instância de Email e uma de SMS. Chame send_notification com cada uma para demonstrar que sua função genérica funciona com qualquer tipo que implemente Notify.

O ponto principal aqui é que send_notification não conhece o tipo concreto que recebe — ele apenas sabe que o tipo pode produzir uma mensagem. A trait bound T: Notify garante essa capacidade.

Sua saída deve mostrar ambas as 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.

Folha de consulta

Um trait bound restringe um parâmetro de tipo genérico para aceitar apenas tipos que implementam uma trait específica.

A sintaxe para trait bounds usa : TraitName após o parâmetro genérico:

fn function_name<T: TraitName>(item: T) {
    // Agora pode chamar métodos de TraitName no item
}

Exemplo com um trait bound:

trait Summary {
    fn summarize(&self) -> String;
}

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());
}

A notação <T: Summary> significa "T é qualquer tipo que implementa Summary." Isso permite que a função chame métodos da trait enquanto permanece genérica.

Experimente você mesmo

mod notification;

use notification::{Email, SMS, send_notification};

fn main() {
    // Ler as 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: Criar uma instância de Email com o assunto
    
    // TODO: Criar uma instância de SMS com o conteúdo
    
    // TODO: Chamar send_notification com o email
    
    // TODO: Chamar send_notification com o sms
}
quiz iconTeste seus conhecimentos

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