Vettori di trait
Fa parte della sezione Programmazione orientata agli oggetti del percorso Rust di Coddy. Lezione 46 di 61.
Hai visto come Box<dyn Trait> permette a una singola variabile di contenere tipi diversi. La vera potenza emerge quando lo combini con le collezioni: memorizzare più tipi diversi in un singolo vettore.
Un vettore regolare richiede che tutti gli elementi siano dello stesso tipo. Non puoi avere un Vec<Dog> che contenga anche dei gatti. Ma con gli oggetti trait puoi creare un vettore che contiene qualsiasi cosa implementi un trait condiviso:
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),
];
}
Il tipo Vec<Box<dyn Speak>> significa «un vettore di oggetti trait allocati in box». Ogni elemento è un Box che punta a qualcosa che implementa Speak: i tipi effettivi possono essere diversi. Avvolgi ogni valore con Box::new() quando lo aggiungi al vettore.
Questo schema è essenziale per creare sistemi flessibili in cui devi gestire raccolte di oggetti correlati ma diversi, come componenti dell'interfaccia utente, entità di gioco o sistemi di plugin.
Sfida
FacileCostruiamo un sistema di notifiche in grado di gestire diversi tipi di avvisi! Creerai una raccolta che memorizza vari tipi di notifiche: email, messaggi SMS e notifiche push, tutte in un unico vettore usando oggetti trait.
Organizzerai il codice in due file:
notifications.rs: definisci un trait pubblicoNotifycon un metodosendche accetta&selfe restituisce unaStringche descrive la notifica inviata. Poi crea tre struct pubbliche:Email: con un campo pubblicorecipient(String). Il suo metodosenddovrebbe restituireEmail to: {recipient}Sms: con un campo pubblicophone(String). Il suo metodosenddovrebbe restituireSMS to: {phone}Push: con un campo pubblicodevice(String). Il suo metodosenddovrebbe restituirePush to: {device}
main.rs: importa il modulo notifications e crea un vettore di tipoVec<Box<dyn Notify>>che contenga tutti e tre i tipi di notifica. Usa gli input forniti per creare una notifica di ciascun tipo, aggiungile al vettore nell'ordine indicato (Email, Sms, Push), quindi stampa il risultato della chiamata asend()sul primo elemento del vettore.
Il concetto chiave è che, nonostante Email, Sms e Push siano struct completamente diverse, possono convivere tutte nello stesso vettore perché condividono il trait Notify. Ogni elemento viene racchiuso con Box::new() per creare l'oggetto trait.
L'output dovrebbe mostrare la prima notifica:
Email to: {recipient}Per esempio, con gli input alice@example.com, 555-1234 e iPhone-12:
Email to: alice@example.comRiceverai tre input: il destinatario dell'email, il numero di telefono e il nome del dispositivo.
Provalo tu
mod notifications;
use notifications::{Notify, Email, Sms, Push};
fn main() {
// Leggi gli input
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: Crea un Vec<Box<dyn Notify>> per contenere diversi tipi di notifiche
// TODO: Crea istanze di Email, Sms e Push usando gli input
// TODO: Aggiungile al vettore nell'ordine (Email, Sms, Push) usando Box::new()
// TODO: Stampa il risultato della chiamata a send() sul primo elemento
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Metodi e comportamento
Introduzione ai blocchi implIl parametro selfMetodi mutabiliFunzioni associateBlocchi impl multipliConcatenamento di metodiRiepilogo - Azioni sul rettangolo4Progetto: animale domestico virtuale
Definire l'animaleNutrire l'animale7Trait standard
Il trait DebugIl trait DisplayClone e CopyTrait di uguaglianzaRiepilogo - Punto stampabile10Progetto: sistema di documenti
Il trait DrawComponente di testo2Incapsulamento e moduli
Nozioni di base sui moduliLa parola chiave publicCampi privatiGetterSetterRiepilogo - Armadietto sicuro5Generics
Struct genericheMetodi genericiPiù tipi genericiFunzioni genericheRipasso - Punto di coordinate8I Trait come vincoli
Sintassi dei vincoli sui traitVincoli multipliLa clausola whereRestituire tipi con i traitRiepilogo - Stampante generica11Modelli di progettazione in Rust
Pattern NewtypeComposizioneIl trait DropFrom e IntoRipasso - Mock con smart pointer3Enumerazioni avanzate
Enumerazioni con datiMetodi sulle enumerazioniCorrispondenza delle varianti con datiL'enumerazione Option ripresaRiepilogo - enumerazione Shape6Definizione dei trait
Che cos’è un trait?Implementare i traitImplementazioni predefiniteSovrascrivere le implementazioni predefiniteTrait con parametriRiepilogo - Lettore multimediale9Oggetti trait
Dispatch dinamicoVettori di traitIterare sugli oggetti traitRiepilogo - Gestore dello zooEsercitati da solo: Compilatore Rust online