Option in Rust
Rust non ha null. Se un valore potrebbe mancare, lo si dice direttamente nel tipo, usando Option. All’inizio sembra una formalità in più, ma chiude la porta a un’intera categoria di bug: il «riferimento nullo» che ha causato innumerevoli crash in altri linguaggi.
Com’è fatto Option
Sezione intitolata “Com’è fatto Option”Option è un’enum con due varianti:
enum Option<T> { Some(T), // c'è un valore None, // nessun valore}In realtà l’hai già incontrato: .get() su un vettore e .get() su una HashMap restituiscono entrambi un Option.
Creare un Option
Sezione intitolata “Creare un Option”fn main() { let has_value: Option<i32> = Some(5); let no_value: Option<i32> = None;
println!("{:?}", has_value); println!("{:?}", no_value);}Output:
Some(5)NoneGestire un Option con match
Sezione intitolata “Gestire un Option con match”Il modo sicuro per tirare fuori il valore è match: Rust ti obbliga a gestire entrambi i casi, quindi non puoi dimenticarti per sbaglio del caso «manca»:
fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("L'età è {value}"), None => println!("Nessuna età indicata"), }}Un esempio realistico
Sezione intitolata “Un esempio realistico”fn find_user(id: u32) -> Option<&'static str> { if id == 1 { Some("Alice") } else { None }}
fn main() { match find_user(1) { Some(name) => println!("Trovato: {name}"), None => println!("Utente non trovato"), }
match find_user(99) { Some(name) => println!("Trovato: {name}"), None => println!("Utente non trovato"), }}Output:
Trovato: AliceUtente non trovatoInvece di restituire null o lanciare un’eccezione quando un utente non c’è, è il tipo di ritorno stesso della funzione a dire a chi la chiama «potrei non trovare niente», e il compilatore non gli permette di ignorare questa possibilità.
Scorciatoie: unwrap_or e unwrap
Sezione intitolata “Scorciatoie: unwrap_or e unwrap”Nei casi semplici, un match completo può sembrare eccessivo. Ecco un paio di scorciatoie:
fn main() { let age: Option<i32> = None;
let value = age.unwrap_or(0); // usa 0 se non c'è un valore println!("{value}");}C’è anche .unwrap(), che ti dà direttamente il valore, ma va in panic (il programma si blocca) se è None:
let age: Option<i32> = Some(30);let value = age.unwrap(); // qui va bene, perché age è SomePratica
Sezione intitolata “Pratica”1. Il primo numero pari
Sezione intitolata “1. Il primo numero pari”Scrivi fn first_even(numbers: &[i32]) -> Option<i32>, che restituisce il primo numero pari della slice, oppure None se non ce n’è nessuno. Stampa il risultato per [3, 7, 8, 10] con match e per [1, 3, 5] con .unwrap_or(0). Suggerimento: scorri la slice e fai return Some(n) appena n % 2 == 0; dopo il ciclo, restituisci None.
Mostra la soluzione
fn first_even(numbers: &[i32]) -> Option<i32> { for &n in numbers { if n % 2 == 0 { return Some(n); } } None}
fn main() { match first_even(&[3, 7, 8, 10]) { Some(n) => println!("Primo pari: {n}"), None => println!("Nessun numero pari"), }
let fallback = first_even(&[1, 3, 5]).unwrap_or(0); println!("Con valore di riserva: {fallback}");}Output:
Primo pari: 8Con valore di riserva: 02. Leggere un’impostazione
Sezione intitolata “2. Leggere un’impostazione”Crea una HashMap di impostazioni che contiene solo "volume" con valore 80. Poi scorri le chiavi "volume" e "luminosità" e stampa il valore se esiste, oppure un messaggio «non impostata» se non c’è. Suggerimento: .get(key) su una HashMap restituisce un Option, quindi puoi farci direttamente match.
Mostra la soluzione
use std::collections::HashMap;
fn main() { let mut settings = HashMap::new(); settings.insert("volume", 80);
for key in ["volume", "luminosità"] { match settings.get(key) { Some(value) => println!("{key} = {value}"), None => println!("{key} non è impostata"), } }}Output:
volume = 80luminosità non è impostataRiepilogo
Sezione intitolata “Riepilogo”Option<T>rappresenta un valore che può esserci oppure no: Rust non hanull.Some(value)contiene qualcosa;Nonevuol dire che non c’è niente.matchè il modo sicuro di gestire entrambi i casi; il compilatore non ti lascia dimenticarne uno..unwrap_or(default)ti dà un valore di riserva;.unwrap()va in panic se il valore èNone, quindi usalo con parsimonia.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Cosa usa Rust al posto di null per un valore che potrebbe mancare?
2. Cosa succede quando chiami .unwrap() su un valore None?
3. Cosa fa age.unwrap_or(0) quando age è None?
4. Perché match è considerato il modo sicuro per estrarre un valore da un Option?
5. Quali metodi citati in questa pagina restituiscono già un Option?
Punteggio: 0 / 5
Per approfondire
Sezione intitolata “Per approfondire”- The Option Enum and Its Advantages Over Null Values — The Rust Book (in inglese): la spiegazione ufficiale del perché Rust usa
Optioninvece dinull. std::option— documentazione della libreria standard (in inglese): una panoramica di tutto quello che puoi fare con unOption, compresi metodi utili comemap,and_theneis_some.- Option & unwrap — Rust by Example (in inglese): brevi esempi eseguibili di
match,unwrape di quando avviene un panic. Option<T>— documentazione std (in inglese): l’elenco completo dei metodi, ognuno con un piccolo esempio da copiare.