Lezioni del modulo (2/2)
Pattern matching, Option e Result
Rust possiede un meccanismo di controllo del flusso estremamente potente chiamato pattern matching. Permette di confrontare un valore con una serie di pattern ed eseguire codice in base al pattern corrispondente.
L'Istruzione match
L'istruzione match in Rust è simile allo switch in altri linguaggi, ma molto più potente e sicura. Il compilatore garantisce che il matching sia esaustivo, ossia che tutti i possibili casi siano gestiti:
enum Direction {
North,
South,
East,
West,
}
let dir = Direction::East;
match dir {
Direction::North => println!("Andiamo a Nord"),
Direction::South => println!("Andiamo a Sud"),
Direction::East => println!("Andiamo a Est"),
Direction::West => println!("Andiamo a Ovest"),
}
Gestire l'assenza di valore: Option
Rust non ha il concetto di valore nullo (null o nil). Al suo posto, la libreria standard definisce un enum chiamato Option<T> che può contenere:
Some(T): contiene un valore di tipoT.None: indica l'assenza di valore.
let x: Option<i32> = Some(5);
let y: Option<i32> = None;
match x {
Some(val) => println!("C'e un valore: {}", val),
None => println!("Nessun valore trovato"),
}
Gestire gli errori ripristinabili: Result
Per operazioni che possono fallire (come la lettura di un file o la conversione di una stringa in numero), Rust usa l'enum Result<T, E> che possiede due varianti:
Ok(T): l'operazione è andata a buon fine e contiene il valore di successoT.Err(E): l'operazione è fallita e contiene l'erroreE.
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> {
if denominator == 0.0 {
Err(String::from("Divisione per zero!"))
} else {
Ok(numerator / denominator)
}
}
Grazie al compilatore di Rust, sei obbligato a gestire il caso Err prima di poter accedere al valore dentro Ok, evitando moltissimi bug a runtime.
Il Pattern Jolly _ (Wildcard)
Quando lavoriamo con tipi di dato che hanno un gran numero di varianti (come gli interi), elencare tutti i casi in un match è impossibile. Rust permette di usare il carattere jolly _ per catturare tutti i casi rimanenti non definiti esplicitamente:
let some_u8 = 3u8;
match some_u8 {
1 => println!("Uno"),
2 => println!("Due"),
_ => println!("Qualcos'altro"), // Gestisce tutti gli altri valori
}
Prova tu
Dichiara una variabile maybe_value di tipo Option<i32> contenente Some(42). Usa un'istruzione match su maybe_value: se contiene Some(v) stampa 'value is: v' (con println!), se contiene None stampa 'no value'.
Mostra suggerimento
Scrivi un blocco `match maybe_value { Some(v) => println!('value is: {}', v), None => println!('no value') }`.
Soluzione disponibile dopo 3 tentativi
Completa la funzione check_age in modo che restituisca Ok('Adult') se age è maggiore o uguale a 18, oppure Err('Minor') in caso contrario.
Mostra suggerimento
Usa un `if age >= 18`per decidere se restituire`Ok('Adult')`oppure`Err('Minor')`.
Soluzione disponibile dopo 3 tentativi
Dichiara un enum chiamato Status contenente le varianti Active e Inactive. Nel main, dichiara una variabile current_status con valore Status::Active. Infine, usa un'istruzione match su current_status per stampare 'active' se lo stato è Active, o 'inactive' se lo stato è Inactive.
Mostra suggerimento
Definisci l'enum prima del main con `enum Status { Active, Inactive }`. Nel main usa `let current_status = Status::Active;`ed esegui il`match` sulle varianti.
Soluzione disponibile dopo 3 tentativi