Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Enum e match in Rust

Rust: utilizzare enum per i casi che richiedono la definizione di un dato che permetta la scelta tra una serie limitata di valori
Rust: utilizzare enum per i casi che richiedono la definizione di un dato che permetta la scelta tra una serie limitata di valori
Link copiato negli appunti

Abbiamo visto sinora quanto i tipi di dato rivestano un'importanza centrale in Rust e di come questo linguaggio ne offra di estremamente variegati e dettagliati. Eppure, ci sono casi che richiedono la definizione di una tipologia di dato che permetta solo la scelta tra una serie limitata di valori: si tratta delle enum, strutture utilissime, presenti in molti linguaggi di programmazione.

Come definire un enum in Rust

Per definire una enum, occorre:

  • introdurre una definizione con la parola chiave enum;
  • scegliere il nome per il nuovo tipo di dato che verrà così generato;
  • elencare tra parentesi graffe i valori che saranno contemplati.

Un esempio piuttosto scolastico ma che può costituire un buon approccio è quello del semaforo. Un semaforo stradale può avere solo tre stati rappresentati ognuno da un valore: rosso, verde o giallo. Se volessimo creare un tipo di dato per gestire un caso simile, una enum sarebbe assolutamente ideale e potrebbe essere definita così:

enum Semaforo{
  Rosso,
  Verde,
  Giallo,
}

Come si vede i valori interni vengono semplicemente elencati ma non viene assegnato niente a nessuno di essi. Per poter utilizzare queste grandezze sarà sufficiente invocarle anteponendo il nome della enum, Semaforo in questo caso, ed una coppia di due punti: Semaforo::Rosso per il rosso, Semaforo::Giallo per il giallo e Semaforo::Verde per il verde.

Controllo del flusso ed enum

La principale attività svolta con le enum è quella di definire un limitato numero di casi tra cui scegliere e gestire variabili dello stesso tipo della enum per utilizzare costrutti per il controllo del flusso. Un caso particolarmente adatto alla gestione delle enum è il costrutto match già incontrato in precedenza. Eccone subito un esempio:

let colore:Semaforo=Semaforo::Verde;
match colore {
  Semaforo::Rosso => println!("Fermati!"),
    Semaforo::Verde => println!("Prego passa pure"),
 Semaforo::Giallo => println!("Frena che tra poco scatta il rosso")
}

Come si vede, il match contempla tre possibili casi, ognuno collegato con un valore dell'enum: una struttura di questo genere permetterà di gestire ogni possibile situazione.

Option

Un caso di enum che può risultare molto utile ed interessante soprattutto perché parte strumentale di Rust è Option. Dà la possibilità di definire valori opzionali il che potrebbe sembrare un aspetto di nicchia ma in realtà risulta fondamentale quando un'operazione potrebbe non restituire un risultato in ogni caso. E' definita così:

enum Option {
    None,
    Some(T),
}

Vediamo che è essenzialmente composta da due valori: None che indica un risultato nullo e Some, l'eventualità che un risultato ci sia.

Per farne uso, impariamo subito due aspetti. Il primo è che Option viene definito specificando il tipo di dato che dovrà gestire in caso di presenza di valore, ad esempio Option<u32> prospetterà la conservazione di valori di tipo u32. Secondo, esistono dei metodi molto importanti che sono is_some per verificare se l'Option contiene qualcosa, is_none per verificare che sia vuoto e, nel caso ci sia un valore custodito, unwrap per estrarlo. Usiamoli in sequenza con questo esempio:

fn main() {
  let mut valore: Option<u32> = Some(2);
  // se un valore esiste, lo stampiamo
  if valore.is_some(){
  println!("Il valore custodito è {}",valore.unwrap());}
  // annulliamo il valore
  valore=None;
  if valore.is_none(){
    println!("Non si può stampare perchè è vuoto!");}
}

Un caso molto utile è quando in Rust dobbiamo definire una funzione che in alcuni casi potrebbe non restituire un risultato. Pensiamo ad esempio a quelle operazioni matematiche impossibili come la divisione per zero. Potremmo creare una funzione in questo modo:

fn divisione(dividendo: f32, divisore: f32) -> Option<f32> {
    if divisore!=0.0 {Some(dividendo / divisore)} else {None}
}

per poi richiamarla nel main in questo modo:

fn main() {
  let dividendo:f32 = 10.0;
  let divisore:f32 = 3.0;
  println!("Quanto fa {} diviso {}? ",dividendo, divisore);
  let risultato:Option<f32> = divisione(dividendo, divisore);
  if risultato.is_none() {
    println!("Impossibile dividere per zero");
  }
  else {
  println!("Risultato: {}",risultato.unwrap());
  }
}

Il risultato stampato sarebbe:

Quanto fa 10 diviso 3?
Risultato: 3.3333333

Qualora cambiassimo il valore del divisore (non è mut perché nelle nostre prove ricompiliamo l'esempio) impostandolo a 0.0 otterremmo questo output:

Quanto fa 10 diviso 0?
Impossibile dividere per zero

Ti consigliamo anche