Vetores de Traits
Parte da seção Programação Orientada a Objetos 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 vários 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 um trait compartilhado:
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 de trait em caixas." Cada elemento é um Box que aponta para algo que implementa Speak: os tipos reais podem ser diferentes. Você envolve cada valor com Box::new() ao adicioná-lo ao vetor.
Este padrão é essencial para criar sistemas flexíveis nos quais você precisa gerenciar coleções de objetos relacionados, mas diferentes, como componentes de UI, entidades de jogos ou sistemas de plugins.
Desafio
FácilVamos criar um sistema de notificações capaz de lidar com diferentes tipos de alertas! Você criará uma coleção que armazena vários tipos de notificações, e-mails, mensagens SMS e notificações push, todos em um único vetor usando objetos de trait.
Você organizará seu código em dois arquivos:
notifications.rs: defina um trait públicoNotifycom um métodosendque recebe&selfe retorna umaStringdescrevendo a notificação que está sendo enviada. Em seguida, crie três structs públicas:Email: com um campo públicorecipient(String). Seusenddeve retornarEmail to: {recipient}Sms: com um campo públicophone(String). Seusenddeve retornarSMS to: {phone}Push: com um campo públicodevice(String). Seusenddeve retornarPush to: {device}
main.rs: importe seu módulo de notificações e crie um vetor do tipoVec<Box<dyn Notify>>que contenha os três tipos de notificações. Use as entradas fornecidas para criar uma notificação de cada tipo, adicione-as ao vetor na ordem (Email, Sms, Push) e, em seguida, imprima o resultado da chamada desend()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 o trait Notify. Cada elemento é encapsulado 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.comVocê receberá três entradas: o destinatário do e-mail, o número de telefone e o nome do dispositivo.
Experimente você mesmo
mod notifications;
use notifications::{Notify, Email, Sms, Push};
fn main() {
// Ler 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 armazenar diferentes tipos de notificação
// TODO: Criar instâncias de Email, Sms e Push usando as entradas
// TODO: Adicioná-los ao vetor na ordem (Email, Sms, Push) usando Box::new()
// TODO: Imprimir o resultado de chamar send() no primeiro elemento
}
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 Player9Objetos Trait
Despacho DinâmicoVetores de TraitsIterando Objetos TraitRecapitulação - Gerenciador de ZoológicoPratique por conta própria: Compilador de Rust online