Sintassi dei vincoli sui trait
Fa parte della sezione Programmazione orientata agli oggetti del percorso Rust di Coddy. Lezione 40 di 61.
Finora hai imparato a definire i trait e a implementarli per le struct. Hai anche lavorato con i generics per scrivere codice flessibile. Ora è il momento di combinare questi concetti: usare i trait per vincolare i tipi che una funzione generica può accettare.
Considera una funzione generica che deve chiamare un metodo specifico sul suo parametro. Senza vincoli, Rust non garantisce che il tipo T abbia quel metodo:
fn print_info<T>(item: T) {
println!("{}", item.summarize()); // ¡Error! T podría no tener summarize()
}
La soluzione è un vincolo di trait. Aggiungendo : TraitName dopo il parametro generico, dici a Rust che T deve implementare quel trait:
trait Summary {
fn summarize(&self) -> String;
}
fn print_info<T: Summary>(item: T) {
println!("{}", item.summarize()); // Ora Rust sa che questo metodo esiste
}
La sintassi <T: Summary> si legge come «T è un tipo qualsiasi che implementa Summary». Ora la funzione accetta solo i tipi che hanno il comportamento richiesto. Se provi a passare un tipo che non implementa Summary, il compilatore lo rifiuterà con un errore chiaro.
Questo schema è potente perché ti permette di scrivere codice generico che ha comunque accesso a funzionalità specifiche. La tua funzione rimane flessibile: funziona con qualsiasi tipo, ma solo con i tipi che forniscono il comportamento di cui hai bisogno.
Sfida
FacileCostruiamo un sistema di notifiche che usa i vincoli dei trait per garantire che possano essere inviati solo messaggi formattati correttamente! Creerai una funzione generica che accetta qualsiasi tipo che implementi un trait specifico, dimostrando come i vincoli dei trait limitano i parametri generici.
Organizzerai il codice in due file:
notification.rs: Definisci un trait pubblicoNotifycon un metodo chiamatomessageche prende&selfe restituisce unaString. Poi crea due struct pubbliche che implementano questo trait:Email: con un campo pubblicosubject(String). Il suo metodomessagedovrebbe restituireEmail: {subject}SMS: con un campo pubblicocontent(String). Il suo metodomessagedovrebbe restituireSMS: {content}
send_notificationche accetta qualsiasi tipoTche implementi il traitNotify. Questa funzione dovrebbe stampare il risultato della chiamata amessage()sull'elemento.main.rs: Includi il modulo delle notifiche e usa gli input forniti per creare un'istanza diEmaile una diSMS. Chiamasend_notificationcon ciascuna per dimostrare che la tua funzione generica funziona con qualsiasi tipo che implementiNotify.
L'idea chiave è che send_notification non conosce il tipo concreto che riceve. Sa solo che il tipo può produrre un messaggio. Il vincolo del trait T: Notify garantisce questa capacità.
L'output dovrebbe mostrare entrambe le notifiche inviate:
Email: {subject}
SMS: {content}Per esempio, con gli input Meeting Tomorrow e On my way!:
Email: Meeting Tomorrow
SMS: On my way!Riceverai due input: l'oggetto dell'email e il contenuto dell'SMS.
Provalo tu
mod notification;
use notification::{Email, SMS, send_notification};
fn main() {
// Leggi gli input
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: Crea un'istanza di Email con subject
// TODO: Crea un'istanza di SMS con content
// TODO: Chiama send_notification con email
// TODO: Chiama send_notification con sms
}
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 multimedialeEsercitati da solo: Compilatore Rust online