Gli enum (enumerazioni) in Rust sono radicalmente diversi dagli enum in C/C++: possono memorizzare dati associati e sono perfetti per modellare stati ed errori. Con essi si costruiscono macchine a stato finito sicure dal punto di vista dei tipi, vari tipi di Option/Result e si realizza il pattern "sum types". Costruzioni analoghe sono storicamente utilizzate nei linguaggi funzionali per descrivere varianti di entità con varianti rigorosamente distinte.
Problema: ottenere espressività (esprimere tutte le varianti di stato), dove ogni caso di gestione è obbligatorio e non è possibile saltare accidentalmente un ramo. Gli errori su scala di progetto sono difficili da tipizzare senza una struttura così espressiva.
Soluzione: l'enum con dati associati e il pattern matching offrono controllo: ogni ramo è verificato dal compilatore, garantendo l'exhaustiveness. Inoltre, per Result e Option sono già stati implementati molti metodi ausiliari.
Esempio di codice:
enum NetworkState { Disconnected, Connecting(u32), // tentativo numero Connected(String), Error(String), } fn print_state(state: NetworkState) { match state { NetworkState::Disconnected => println!("Net: disconnected"), NetworkState::Connecting(count) => println!("Net: connecting (tentativo {})", count), NetworkState::Connected(addr) => println!("Net: connesso a {}", addr), NetworkState::Error(msg) => println!("Errore di rete: {}", msg), } }
Caratteristiche chiave:
È possibile elaborare parzialmente i rami enum senza _?
Il compilatore vieta i casi non chiusi per enum non esaustivi, ma se si utilizza _, i rami non elaborati saranno "assorbiti". È consigliabile evitare _ per rami critici affinché modifiche future non rimangano inosservate.
In quali casi i valori associati vengono riferiti e in quali vengono copiati durante il pattern matching?
Durante il pattern matching, i dati associati vengono spostati (move) per impostazione predefinita. Se è solo necessario visualizzare, utilizzare i riferimenti:
match &state { NetworkState::Connected(addr) => println!("per riferimento: {}", addr), _ => {} }
È possibile utilizzare due enum con varianti sovrapposte per nome in una stessa struttura?
Sì, ma i nomi delle varianti vengono utilizzati con il prefisso dell'enum. Questo esclude le collisioni e rende il codice autodocumentante (ad esempio, Status::Ok vs NetworkState::Ok).
_ per nascondere sempre nuove varianti quando si espande l'enum_ =>) nei gestori di errori criticiNel codice, la gestione di Result<T, E> ha sempre un catch-all tramite _ =>, e nuovi errori (quando si espande l'enum) passano inosservati — si verificano silent-loss di errori.
Vantaggi:
Svantaggi:
Viene utilizzato l'exhaustiveness matching, ogni variante Enum è gestita esplicitamente, oppure si verifica un panic in un ramo per cui non è presente un comportamento definito.
Vantaggi:
Svantaggi: