Salta ai contenuti

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.

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.

fn main() {
let has_value: Option<i32> = Some(5);
let no_value: Option<i32> = None;
println!("{:?}", has_value);
println!("{:?}", no_value);
}

Output:

Some(5)
None
▶ Provalo tu

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"),
}
}
▶ Provalo tu
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: Alice
Utente non trovato
▶ Provalo tu

Invece 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à.

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}");
}
▶ Provalo tu

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 è Some

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: 8
Con valore di riserva: 0
▶ Provalo tu

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 = 80
luminosità non è impostata
▶ Provalo tu
  • Option<T> rappresenta un valore che può esserci oppure no: Rust non ha null.
  • Some(value) contiene qualcosa; None vuol 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

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