Menu
Coddy logo textTech

Vetores de Traits

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

Você viu como Box<dyn Trait> permite que uma única variável contenha tipos diferentes. O verdadeiro poder surge quando você combina isso com coleções — armazenando múltiplos tipos diferentes em um único vetor.

Um vetor comum exige que todos os elementos sejam do mesmo tipo. Você não pode ter um Vec<Dog> que também contenha gatos. Mas com objetos de trait, você pode criar um vetor que contenha qualquer coisa que implemente uma trait compartilhada:

trait Speak {
    fn speak(&self) -> String;
}

struct Dog;
struct Cat;
struct Bird;

impl Speak for Dog {
    fn speak(&self) -> String { String::from("Woof!") }
}

impl Speak for Cat {
    fn speak(&self) -> String { String::from("Meow!") }
}

impl Speak for Bird {
    fn speak(&self) -> String { String::from("Tweet!") }
}

fn main() {
    let animals: Vec<Box<dyn Speak>> = vec![
        Box::new(Dog),
        Box::new(Cat),
        Box::new(Bird),
    ];
}

O tipo Vec<Box<dyn Speak>> significa "um vetor de objetos trait em boxes". Cada elemento é um Box apontando para algo que implementa Speak—os tipos reais podem diferir. Você envolve cada valor com Box::new() ao adicioná-lo ao vetor.

Este padrão é essencial para construir sistemas flexíveis onde você precisa gerenciar coleções de objetos relacionados, mas diferentes — como componentes de UI, entidades de jogo ou sistemas de plugins.

challenge icon

Desafio

Fácil

Vamos construir um sistema de notificação que possa lidar com diferentes tipos de alertas! Você criará uma coleção que armazena vários tipos de notificação — e-mails, mensagens SMS e notificações push — tudo em um único vetor usando objetos de trait.

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

  • notifications.rs: Defina uma trait pública Notify com um método send que recebe &self e retorna uma String descrevendo a notificação que está sendo enviada. Em seguida, crie três structs públicas:
    • Email — com um campo público recipient (String). Seu send deve retornar "Email to: {recipient}"
    • Sms — com um campo público phone (String). Seu send deve retornar "SMS to: {phone}"
    • Push — com um campo público device (String). Seu send deve retornar "Push to: {device}"
  • main.rs: Importe seu módulo de notificações e crie um vetor do tipo Vec<Box<dyn Notify>> que contenha todos os três tipos de notificação. Use as entradas fornecidas para criar um de cada tipo de notificação, adicione-os ao seu vetor na ordem (Email, Sms, Push) e, em seguida, imprima o resultado da chamada de send() no primeiro elemento do vetor.

O conceito principal aqui é que, apesar de Email, Sms e Push serem structs completamente diferentes, todos podem coexistir no mesmo vetor porque compartilham a trait Notify. Cada elemento é envolvido com Box::new() para criar o objeto de trait.

Sua saída deve mostrar a primeira notificação:

Email to: {recipient}

Por exemplo, com as entradas "alice@example.com", "555-1234" e "iPhone-12":

Email to: alice@example.com

Você receberá três entradas: o destinatário do e-mail, o número de telefone e o nome do dispositivo.

Folha de consulta

Um Vec<Box<dyn Trait>> permite armazenar múltiplos tipos diferentes em um único vetor, desde que todos implementem a mesma trait.

Vetores regulares exigem que todos os elementos sejam do mesmo tipo, mas objetos de trait (trait objects) permitem coleções heterogêneas:

trait Speak {
    fn speak(&self) -> String;
}

struct Dog;
struct Cat;

impl Speak for Dog {
    fn speak(&self) -> String { String::from("Woof!") }
}

impl Speak for Cat {
    fn speak(&self) -> String { String::from("Meow!") }
}

fn main() {
    let animals: Vec<Box<dyn Speak>> = vec![
        Box::new(Dog),
        Box::new(Cat),
    ];
}

Cada elemento é envolvido com Box::new() ao ser adicionado ao vetor. Os tipos reais podem ser diferentes, desde que implementem a trait compartilhada.

Experimente você mesmo

mod notifications;

use notifications::{Notify, Email, Sms, Push};

fn main() {
    // Ler as entradas
    let mut recipient = String::new();
    std::io::stdin().read_line(&mut recipient).expect("Failed to read line");
    let recipient = recipient.trim().to_string();

    let mut phone = String::new();
    std::io::stdin().read_line(&mut phone).expect("Failed to read line");
    let phone = phone.trim().to_string();

    let mut device = String::new();
    std::io::stdin().read_line(&mut device).expect("Failed to read line");
    let device = device.trim().to_string();

    // TODO: Criar um Vec<Box<dyn Notify>> para conter diferentes tipos de notificação
    // TODO: Criar instâncias de Email, Sms e Push usando as entradas
    // TODO: Adicioná-las ao vetor em ordem (Email, Sms, Push) usando Box::new()
    // TODO: Imprimir o resultado da chamada de send() no primeiro elemento
}
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