SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI
Realizzare un Modello di Progettazione Orientato agli Oggetti
Il pattern di stato è un modello di progettazione orientato agli oggetti. La parte centrale del modello è definire un insieme di stati che un valore può avere internamente. Gli stati sono rappresentati da un insieme di oggetti di stato, e il comportamento del valore cambia in base al suo stato. Vedremo un esempio di una struttura di post di blog che ha un campo per contenere il suo stato, che sarà un oggetto di stato tra “bozza”, “revisione” o “pubblicato”.
Gli oggetti di stato condividono funzionalità: in Rust, ovviamente, usiamo strutture e tratti invece di oggetti e ereditarietà. Ogni oggetto di stato è responsabile del proprio comportamento e di regolare quando dovrebbe trasformarsi in un altro stato. Il valore che contiene un oggetto di stato non sa nulla del diverso comportamento degli stati o quando passare da uno stato all’altro.
Il vantaggio nell’usare il pattern di stato è che, quando i requisiti aziendali del programma cambiano, non sarà necessario modificare il codice del valore che tiene lo stato o il codice che usa il valore. Dovremo solo aggiornare il codice all’interno di uno degli oggetti di stato per cambiare le sue regole o forse aggiungere più oggetti di stato.
Prima, implementeremo il pattern di stato in modo più tradizionale orientato agli oggetti, poi useremo un approccio un po’ più naturale in Rust. Approfondiremo l’implementazione progressiva di un flusso di lavoro del post del blog usando il pattern di stato.
La funzionalità finale sarà così:
- Un post di blog inizia come una bozza vuota.
- Quando la bozza è pronta, viene richiesta una revisione del post.
- Quando il post è approvato, viene pubblicato.
- Solo i post di blog pubblicati restituiscono il contenuto da stampare, quindi i post non approvati non possono essere pubblicati accidentalmente.
Qualsiasi altro cambiamento tentato su un post non dovrebbe avere alcun effetto. Ad esempio, se proviamo ad approvare un post di blog prima di aver richiesto una revisione, il post dovrebbe rimanere una bozza non pubblicata.
Vogliamo consentire all’utente di creare un nuovo post di blog in bozza con Post::new. Vogliamo consentire l’aggiunta di testo al post del blog. Se proviamo a ottenere il contenuto del post immediatamente, prima dell’approvazione, non dovremmo ottenere alcun testo perché il post è ancora una bozza. Abbiamo aggiunto assert_eq! nel codice a scopo dimostrativo. Un ottimo test unitario per questo sarebbe affermare che un post di blog in bozza restituisce una stringa vuota dal metodo di contenuto, ma non scriveremo test per questo esempio.
Successivamente, vogliamo abilitare una richiesta di revisione del post, e vogliamo che il contenuto restituisca una stringa vuota mentre si attende la revisione. Quando il post riceve l’approvazione, dovrebbe essere pubblicato, il che significa che il testo del post verrà restituito quando viene chiamato il contenuto.
Nota che l’unico tipo con cui interagiamo dalla crate è il tipo Post. Questo tipo utilizzerà il pattern di stato e conterrà un valore che sarà uno dei tre oggetti di stato rappresentanti i vari stati in cui può trovarsi un post – bozza, in attesa di revisione, o pubblicato. Il passaggio da uno stato all’altro sarà gestito internamente all’interno del tipo Post. Gli stati cambiano in risposta ai metodi chiamati dagli utenti della nostra libreria sull’istanza Post, ma non devono gestire direttamente i cambiamenti di stato. Inoltre, gli utenti non possono commettere errori con gli stati, come pubblicare un post prima che venga revisionato.
Definizione di Post e Creazione di una Nuova Istanza nello Stato di Bozza
Iniziamo con l’implementazione della libreria! Sappiamo di aver bisogno di una struttura di Post pubblica che contenga qualche contenuto, quindi inizieremo con la definizione della struttura e una funzione pubblica associata new per creare un’istanza di Post, come mostrato nel Listato 17-12. Faremo anche un tratto di stato privato che definirà il comportamento che tutti gli oggetti di stato per un Post devono avere.
Quindi Post conterrà un oggetto di tratto di Box<dyn State> all’interno di un Option<T> in un campo privato chiamato stato per contenere l’oggetto di stato. Vedrai perché l’Option<T> è necessaria tra poco.
Memorizzazione del Testo del Contenuto del Post
Abbiamo visto nel Listato 17-11 che vogliamo poter chiamare un metodo chiamato add_text e passargli un &str che viene poi aggiunto come contenuto testuale del post del blog. Implementiamo questo come un metodo, piuttosto che esporre il campo content come pub, in modo che più tardi possiamo implementare un metodo che controlli come i dati del campo content vengono letti. Il metodo add_text è piuttosto semplice, quindi aggiungiamo l’implementazione nel Listato 17-13 al blocco impl Post:
rust
impl Post {
// --snip--
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
}
Il metodo add_text prende un riferimento mutabile a self, perché stiamo modificando l’istanza di Post su cui chiamiamo add_text. Quindi chiamiamo push_str sulla Stringa in content e passiamo l’argomento di testo da aggiungere al contenuto salvato. Questo comportamento non dipende dallo stato del post, quindi non fa parte del pattern di stato. Il metodo add_text non interagisce affatto con il campo state, ma fa parte del comportamento che vogliamo supportare.
Garantire che il Contenuto di un Post in Bozza Sia Vuoto
Anche dopo aver chiamato add_text e aggiunto un po’ di contenuto al nostro post, vogliamo comunque che il metodo content restituisca una slice di stringa vuota perché il post è ancora nello stato di bozza, come mostrato alla riga 7 del Listato 17-11. Per ora, implementiamo il metodo content con la cosa più semplice che soddisfi questo requisito: restituire sempre una slice di stringa vuota. Cambieremo questo più avanti una volta implementata la possibilità di cambiare lo stato di un post in modo che possa essere pubblicato. Finora, i post possono essere solo nello stato di bozza, quindi il contenuto del post dovrebbe sempre essere vuoto. Il Listato 17-14 mostra questa implementazione provvisoria:
rust
impl Post {
// --snip--
pub fn content(&self) -> &str {
""
}
}
Con questo metodo content aggiunto, tutto nel Listato 17-11 fino alla riga 7 funziona come previsto.
Richiedere una Revisione del Post Cambia il Suo Stato
Successivamente, dobbiamo aggiungere la funzionalità per richiedere una revisione di un post, che dovrebbe cambiare il suo stato da Bozza a In Revisione. Il Listato 17-15 mostra questo codice:
rust
impl Post {
// --snip--
pub fn request_review(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.request_review())
}
}
} trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
} struct Draft {} impl State for Draft {
fn request_review(self: Box<Self>) -> Box<dyn State> {
Box::new(PendingReview {})
}
} struct PendingReview {}
impl State for PendingReview {
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
}
Diamo a Post un metodo pubblico chiamato request_review che prenderà un riferimento mutabile a self. Poi chiamiamo un metodo interno request_review sullo stato attuale di Post, e questo secondo metodo request_review consuma lo stato corrente e restituisce un nuovo stato.
Aggiungiamo il metodo request_review al tratto di stato; tutti i tipi che implementano il tratto dovranno ora implementare il metodo request_review. Nota che anziché avere self, &self o &mut self come primo parametro del metodo, abbiamo self: Box<Self>. Questa sintassi significa che il metodo è valido solo quando chiamato su una Box che contiene il tipo. Questa sintassi prende il possesso di Box<Self>, invalidando il vecchio stato in modo che il valore di stato di Post possa trasformarsi in un nuovo stato.
Per consumare il vecchio stato, il metodo request_review deve prendere il possesso del valore di stato. Questo è dove entra in gioco l’Option nel campo di stato di Post: chiamiamo il metodo take per prendere il valore Some dal campo di stato e lasciare un None al suo posto, perché Rust non ci permette di avere campi non popolati nelle strutture. Questo ci consente di spostare il valore di stato da Post piuttosto che prenderlo in prestito. Poi imposteremo il valore di stato del post sul risultato di questa operazione.
Abbiamo bisogno di impostare state su None temporaneamente anziché impostarlo direttamente con il codice come self.state = self.state.request_review(); per ottenere il possesso del valore di stato. Questo assicura che Post non possa utilizzare il vecchio valore di stato dopo averlo trasformato in un nuovo stato.
Il metodo request_review su Bozza restituisce una nuova istanza incapsulata di una nuova struttura PendingReview, che rappresenta lo stato quando un post è in attesa di una revisione. La struttura PendingReview implementa anche il metodo request_review ma non effettua alcuna trasformazione. Piuttosto, restituisce se stessa, perché quando richiediamo una revisione su un post già nello stato di PendingReview, dovrebbe rimanere nello stato di PendingReview.
Ora possiamo iniziare a vedere i vantaggi del pattern di stato: il metodo request_review su Post è lo stesso indipendentemente dal valore di stato. Ogni stato è responsabile delle proprie regole.
Lasciamo il metodo content su Post così com’è, restituendo una slice di stringa vuota. Ora possiamo avere un Post anche nello stato di PendingReview oltre che nello stato di Bozza, ma vogliamo lo stesso comportamento nello stato di PendingReview. Il Listato 17-11 funziona ora fino alla riga 10!
Aggiunta di approve per Cambiare il Comportamento del Contenuto
Il metodo approve sarà simile al metodo request_review: imposterà lo stato sul valore che lo stato corrente dice dovrebbe avere quando quello stato viene approvato, come mostrato nel Listato 17-16:
rust
impl Post {
// --snip--
pub fn approve(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.approve())
}
}
} trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
fn approve(self: Box<Self>) -> Box<dyn State>;
} struct Draft {} impl State for Draft {
// --snip--
fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
} struct PendingReview {} impl State for PendingReview {
// --snip--
fn approve(self: Box<Self>) -> Box<dyn State> {
Box::new(Published {})
}
} struct Published {} impl State for Published {
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
}
Aggiungiamo il metodo approve al tratto di stato e aggiungiamo una nuova struttura che implementa lo stato, lo stato Pubblicato.
Simile al modo in cui funziona request_review su PendingReview, se chiamiamo il metodo approve su una Bozza, non avrà alcun effetto perché approve restituirà se stessa. Quando chiamiamo approve su PendingReview, restituisce una nuova istanza incapsulata della struttura Pubblicato. La struttura Pubblicato implementa il tratto di stato, e per entrambi i metodi request_review e approve, restituisce se stessa, perché il post dovrebbe rimanere nello stato di Pubblicato in quei casi.
Ora dobbiamo aggiornare il metodo content su Post. Vogliamo che il valore restituito da content dipenda dallo stato corrente di Post, quindi faremo in modo che Post delega a un metodo content definito sul suo stato, come mostrato nel Listato 17-17:
rust
impl Post {
// --snip--
pub fn content(&self) -> &str {
self.state.as_ref().unwrap().content(self)
}
// --snip--
}
Poiché l’obiettivo è mantenere tutte queste regole all’interno delle strutture che implementano lo stato, chiamiamo un metodo content sul valore in stato e passiamo l’istanza di post (cioè self) come argomento. Quindi restituiamo il valore restituito dall’uso del metodo content sul valore di stato.
Chiamiamo il metodo as_ref sull’Option perché vogliamo un riferimento al valore all’interno dell’Option piuttosto che il possesso del valore. Poiché stato è un’Option<Box<dyn State>>, quando chiamiamo as_ref, viene restituita un’Option<&Box<dyn State>>. Se non chiamassimo as_ref, otterremmo un errore perché non possiamo spostare stato dal &self preso in prestito del parametro di funzione.
Quindi chiamiamo il metodo unwrap, che sappiamo non genererà mai un panico, perché sappiamo che i metodi su Post assicurano che stato conterrà sempre un valore Some quando quei metodi saranno finiti. Questo è uno dei casi di cui abbiamo parlato nella sezione “Casi in cui si dispone di più informazioni rispetto al compilatore” del Capitolo 9 quando sappiamo che un valore None non è mai possibile, anche se il compilatore non è in grado di capirlo.
A questo punto, quando chiamiamo content sul &Box<dyn State>, entrerà in vigore la coercizione di dereferenziazione sul & e il Box quindi il metodo content verrà chiamato alla fine sul tipo che implementa il tratto di stato. Questo significa che dobbiamo aggiungere il content al tratto di stato definizione, ed è lì che metteremo la logica su quale contenuto restituire a seconda dello stato che abbiamo, come mostrato nel Listato 17-18:
rust
trait State {
// --snip--
fn content<'a>(&self, post: &'a Post) -> &'a str {
""
}
} // --snip--
struct Published {}
impl State for Published {
// --snip--
fn content<'a>(&self, post: &'a Post) -> &'a str {
&post.content
}
}
Aggiungiamo un’implementazione predefinita per il metodo content che restituisce una slice di stringa vuota. Questo significa che non dobbiamo implementare content su Draft e PendingReview strutture. La struttura Pubblicato sovrascriverà il metodo content e restituirà il valore in post.content.
Nota che abbiamo bisogno di annotazioni di lifetime su questo metodo, come abbiamo discusso nel Capitolo 10. Stiamo prendendo un riferimento a un post come argomento e restituendo un riferimento a una parte di quel post, quindi il lifetime del riferimento restituito è correlato al lifetime dell’argomento post.
E siamo arrivati alla fine! Tutto il Listato 17-11 funziona ora! Abbiamo implementato il pattern di stato con le regole del flusso di lavoro del post del blog. La logica relativa alle regole vive negli oggetti di stato anziché essere dispersa in tutto Post.
rust
Perché Non un Enum?
Potresti averti chiesto perché non abbiamo usato un enum con i diversi stati possibili del post come varianti. Questa è certamente una soluzione possibile, provaci e confronta i risultati finali per vedere quale preferisci! Uno svantaggio nell'usare un enum è che ogni luogo che controlla il valore dell'enum avrà bisogno di un'espressione di match o simile per gestire ogni possibile variante. Questo potrebbe diventare più ripetitivo rispetto a questa soluzione dell'oggetto di tratto.
Trade-offs del Pattern di Stato
Abbiamo dimostrato che Rust è in grado di implementare il pattern di stato orientato agli oggetti per incapsulare i diversi tipi di comportamento che un post dovrebbe avere in ogni stato. I metodi su Post non sanno nulla dei vari comportamenti. Nel modo in cui abbiamo organizzato il codice, dobbiamo guardare solo un posto per conoscere i diversi modi in cui un post pubblicato può comportarsi: l’implementazione del tratto State sulla struttura Published.
Se dovessimo creare un’implementazione alternativa che non usasse il pattern di stato, potremmo invece usare espressioni di match nei metodi su Post o anche nel codice principale che controlla lo stato del post e cambia comportamento in quei posti. Ciò significherebbe che dovremmo guardare diversi posti per capire tutte le implicazioni di un post che si trova nello stato pubblicato! Questo aumenterebbe solo aggiungendo più stati: ogni espressione di match avrebbe bisogno di un’altra opzione.
Con il pattern di stato, i metodi di Post e i posti in cui usiamo Post non hanno bisogno di espressioni di match, e per aggiungere un nuovo stato, dovremmo solo aggiungere una nuova struttura e implementare i metodi del tratto su quella singola struttura.
L’implementazione del pattern di stato è facile da estendere per aggiungere più funzionalità. Per vedere la semplicità nel mantenere il codice che usa il pattern di stato, prova alcuni di questi suggerimenti:
- Aggiungi un metodo reject che cambia lo stato del post da PendingReview a Bozza.
- Richiedi due chiamate per approvare prima che lo stato possa essere cambiato in Pubblicato.
- Permetti agli utenti di aggiungere solo contenuti di testo quando un post è in stato di Bozza. Suggerimento: l’oggetto di stato è responsabile di ciò che potrebbe cambiare sul contenuto ma non responsabile della modifica di Post.
Uno svantaggio del pattern di stato è che, poiché gli stati implementano le transizioni tra gli stati, alcuni degli stati sono accoppiati tra loro. Se aggiungiamo un altro stato tra PendingReview e Published, come Scheduled, dovremmo cambiare il codice in PendingReview per passare a Scheduled invece. Sarebbe meno lavoro se PendingReview non dovesse cambiare con l’aggiunta di un nuovo stato, ma questo significherebbe passare a un altro pattern di progettazione.
Un altro svantaggio è che abbiamo duplicato alcune logiche. Per eliminare parte della duplicazione, potremmo cercare di creare implementazioni predefinite per i metodi request_review e approve sul tratto State che restituiscono self; tuttavia, ciò violerebbe la sicurezza dell’oggetto, poiché il tratto non sa esattamente quale sarà il self concreto. Vogliamo essere in grado di usare State come oggetto di tratto, quindi i suoi metodi devono essere sicuri per l’oggetto.
Altra duplicazione include le implementazioni simili dei metodi request_review e approve su Post. Entrambi i metodi delegano all’implementazione dello stesso metodo sul valore nel campo di stato di Option e impostano il nuovo valore del campo di stato sul risultato. Se avessimo molti metodi su Post che seguono questo pattern, potremmo considerare di definire una macro per eliminare la ripetizione (vedi la sezione “Macro” nel Capitolo 19).
Implementando esattamente il pattern di stato come è definito per i linguaggi orientati agli oggetti, non stiamo sfruttando appieno i punti di forza di Rust come potremmo. Vediamo alcune modifiche che possiamo apportare alla crate del blog che possono rendere gli stati e le transizioni non validi errori di compilazione. Codifica degli Stati e del Comportamento come Tipi
Ti mostreremo come ripensare il pattern di stato per ottenere un diverso insieme di trade-off. Piuttosto che incapsulare gli stati e le transizioni completamente in modo che il codice esterno non ne abbia alcuna conoscenza, codificheremo gli stati in tipi diversi. Di conseguenza, il sistema di controllo dei tipi di Rust impedirà i tentativi di utilizzare post in bozza dove sono consentiti solo post pubblicati emettendo un errore del compilatore.
Consideriamo la prima parte di main nel Listato 17-11:
rust
fn main() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content());
}
Abilitiamo ancora la creazione di nuovi post in stato di bozza utilizzando Post::new e la possibilità di aggiungere testo al contenuto del post. Ma anziché avere un metodo content su un post in bozza che restituisce una stringa vuota, faremo in modo che i post in bozza non abbiano affatto il metodo content. In questo modo, se proviamo a ottenere il contenuto di un post in bozza, otterremo un errore del compilatore che ci dice che il metodo non esiste. Di conseguenza, sarà impossibile mostrare accidentalmente il contenuto del post in bozza in produzione, perché quel codice non verrà nemmeno compilato. Il Listato 17-19 mostra la definizione di una struttura Post e una struttura DraftPost, così come i metodi su ciascuna:
rust
pub struct Post {
content: String,
} pub struct DraftPost {
content: String,
} impl Post {
pub fn new() -> DraftPost {
DraftPost {
content: String::new(),
}
} pub fn content(&self) -> &str {
&self.content
}
}
impl DraftPost {
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
}
Sia le strutture Post che DraftPost hanno un campo di contenuto privato che memorizza il testo del post del blog. Le strutture non hanno più il campo di stato perché stiamo spostando la codifica dello stato nei tipi delle strutture. La struttura Post rappresenterà un post pubblicato e ha un metodo content che restituisce il contenuto.
Abbiamo ancora una funzione Post::new, ma anziché restituire un’istanza di Post, restituisce un’istanza di DraftPost. Poiché content è privato e non ci sono funzioni che restituiscono Post, al momento non è possibile creare un’istanza di Post.
La struttura DraftPost ha un metodo add_text, quindi possiamo aggiungere testo a content come prima, ma nota che DraftPost non ha un metodo content definito! Quindi ora il programma assicura che tutti i post inizino come post in bozza e i post in bozza non abbiano il loro contenuto disponibile per la visualizzazione. Qualsiasi tentativo di aggirare questi vincoli comporterà un errore del compilatore.
Implementazione delle Transizioni come Trasformazioni in Diversi Tipi
Quindi come otteniamo un post pubblicato? Vogliamo imporre la regola che un post in bozza debba essere revisionato e approvato prima di poter essere pubblicato. Un post in stato di revisione in sospeso non dovrebbe ancora mostrare alcun contenuto. Implementiamo questi vincoli aggiungendo un’altra struttura, PendingReviewPost, definendo il metodo request_review su DraftPost per restituire un PendingReviewPost e definendo un metodo approve su PendingReviewPost per restituire un Post, come mostrato nel Listato 17-20:
rust
impl DraftPost {
// --snip--
pub fn request_review(self) -> PendingReviewPost {
PendingReviewPost {
content: self.content,
}
}
} pub struct PendingReviewPost {
content: String,
}
impl PendingReviewPost {
pub fn approve(self) -> Post {
Post {
content: self.content,
}
}
}
I metodi request_review e approve prendono possesso di self, consumando così le istanze di DraftPost e PendingReviewPost e trasformandole rispettivamente in un PendingReviewPost e un Post pubblicato. In questo modo, non avremo istanze di DraftPost persistenti dopo aver chiamato request_review su di esse, e così via. La struttura PendingReviewPost non ha un metodo content definito su di essa, quindi tentare di leggere il suo contenuto comporta un errore del compilatore, come con DraftPost. Poiché l’unico modo per ottenere un’istanza di Post pubblicata che ha un metodo content definito è chiamare il metodo approve su un PendingReviewPost, e l’unico modo per ottenere un PendingReviewPost è chiamare il metodo request_review su un DraftPost, ora abbiamo codificato il flusso di lavoro del post del blog nel sistema di tipi.
Ma dobbiamo anche apportare alcune piccole modifiche a main. I metodi request_review e approve restituiscono nuove istanze anziché modificare la struttura su cui vengono chiamati, quindi dobbiamo aggiungere più let post = shadowing assignments per salvare le istanze restituite. Non abbiamo nemmeno bisogno che le asserzioni sul contenuto dei post in bozza e in revisione pendente siano stringhe vuote, né ne abbiamo bisogno: non possiamo compilare il codice che prova a utilizzare il contenuto dei post in quegli stati più. Il codice aggiornato in main è mostrato nel Listato 17-21:
rust
use blog::Post;
fn main() {
let mut post = Post::new(); post.add_text("I ate a salad for lunch today"); let post = post.request_review(); let post = post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
Le modifiche che abbiamo dovuto apportare a main per riattribuire post significano che questa implementazione non segue esattamente più il pattern di stato orientato agli oggetti: le trasformazioni tra gli stati non sono più completamente incapsulate all’interno dell’implementazione di Post. Tuttavia, il nostro guadagno è che gli stati non validi sono ora impossibili a causa del sistema di tipi e del controllo dei tipi che avviene a tempo di compilazione! Questo assicura che determinati bug, come la visualizzazione del contenuto di un post non pubblicato, saranno scoperti prima di arrivare in produzione.
Prova i compiti suggeriti all’inizio di questa sezione sulla crate del blog così com’è dopo il Listato 17-21 per vedere cosa pensi del design di questa versione del codice. Nota che alcuni dei compiti potrebbero essere già completati in questo design.
Abbiamo visto che anche se Rust è in grado di implementare modelli di progettazione orientati agli oggetti, altri modelli, come la codifica dello stato nel sistema di tipi, sono disponibili anche in Rust. Questi modelli hanno trade-off diversi. Anche se potresti essere molto familiare con i modelli orientati agli oggetti, ripensare il problema per sfruttare le caratteristiche di Rust può fornire vantaggi, come prevenire alcuni bug a tempo di compilazione. I modelli orientati agli oggetti non saranno sempre la soluzione migliore in Rust a causa di alcune caratteristiche, come il possesso, che i linguaggi orientati agli oggetti non hanno. Sommario
Indipendentemente dal fatto che tu pensi che Rust sia un linguaggio orientato agli oggetti dopo aver letto questo capitolo, ora sai che puoi usare gli oggetti di tratto per ottenere alcune caratteristiche orientate agli oggetti in Rust. Il dispatch dinamico può dare al tuo codice una certa flessibilità in cambio di un po’ di prestazioni a tempo di esecuzione. Puoi usare questa flessibilità per implementare modelli di progettazione orientati agli oggetti che possono aiutare la manutenibilità del tuo codice. Rust ha anche altre caratteristiche, come il possesso, che i linguaggi orientati agli oggetti non hanno. Un modello orientato agli oggetti non sarà sempre il modo migliore per sfruttare i punti di forza di Rust, ma è un’opzione disponibile.
Prossimamente, esamineremo i modelli, che sono un’altra delle caratteristiche di Rust che consentono molta flessibilità. Li abbiamo esaminati brevemente in tutto il libro ma non abbiamo ancora visto la loro piena capacità. Andiamo!