If let e while let in Rust
Hai usato match per gestire Option e Result. Il più delle volte, però, ti interessa davvero un solo pattern e vuoi ignorare tutto il resto. Scrivere un match completo con un ramo _ => {} solo per non fare niente funziona, ma è più cerimonia di quanta ne serva. if let è la scorciatoia.
Il problema di un match completo in questo caso
Sezione intitolata “Il problema di un match completo in questo caso”fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("L'età è {value}"), _ => {}, // questo caso in realtà non ci interessa }}Quel _ => {} non fa altro che soddisfare la regola del match per cui «ogni caso deve essere gestito». Se vuoi solo agire su Some, è più codice di quanto l’idea meriti.
fn main() { let age: Option<i32> = Some(30);
if let Some(value) = age { println!("L'età è {value}"); }}Output:
L'età è 30Leggilo così: «se age corrisponde al pattern Some(value), esegui questo blocco, con value legato a quello che c’era dentro». Se age è None, il blocco viene semplicemente saltato: nessun errore, niente stampato.
Aggiungere un else
Sezione intitolata “Aggiungere un else”if let può avere un else, proprio come un normale if:
fn main() { let age: Option<i32> = None;
if let Some(value) = age { println!("L'età è {value}"); } else { println!("Nessuna età indicata"); }}if let con Result
Sezione intitolata “if let con Result”Funziona allo stesso modo con Result, che hai conosciuto nella pagina Result:
fn main() { let input = "42";
if let Ok(number) = input.parse::<i32>() { println!("Convertito: {number}"); } else { println!("Impossibile convertirlo"); }}Concatenare con else if let
Sezione intitolata “Concatenare con else if let”Proprio come if ha else if, if let può essere seguito da else if let. Rust prova ogni pattern a turno ed esegue il primo blocco che corrisponde. È comodo quando hai più posti in cui cercare un valore, in ordine di preferenza:
fn main() { let nickname: Option<&str> = None; let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname { println!("Ciao, {nick}!"); } else if let Some(name) = full_name { println!("Buongiorno, {name}."); } else { println!("Buongiorno, sconosciuto."); }}Output:
Buongiorno, Ferris Crab.nickname è None, quindi il primo blocco viene saltato. full_name corrisponde a Some(name), quindi viene eseguito quel blocco, e l’else finale non viene mai raggiunto. Puoi anche inserire una condizione normale, come else if is_admin { ... }, in qualsiasi punto della catena.
Ogni variabile esiste solo dentro il proprio blocco: nick non si può usare nel secondo blocco, e name non si può usare dopo la fine della catena. Se ti serve il valore spacchettato per tutto il resto del codice, guarda let-else nella pagina Pattern matching.
while let
Sezione intitolata “while let”while let applica la stessa idea a un ciclo: continua a girare finché un valore corrisponde a un pattern. È particolarmente comodo per svuotare una collezione:
fn main() { let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() { println!("{top}"); }}Output:
321.pop() restituisce Some(valore) finché c’è ancora qualcosa, e None quando il vettore è vuoto: a quel punto while let ferma il ciclo da solo. Non serve controllare la lunghezza a mano.
if let o match: quale usare
Sezione intitolata “if let o match: quale usare”Usa match quando hai davvero bisogno di gestire più casi in modi diversi. Passa a if let quando c’è un solo pattern che ti interessa e tutto il resto va semplicemente ignorato. Non sostituisce match: è solo un modo più breve di scrivere il caso comune «mi interessa un solo ramo».
Pratica
Sezione intitolata “Pratica”1. Somma di una pila
Sezione intitolata “1. Somma di una pila”Parti da let mut numbers = vec![4, 8, 15]; e da un totale progressivo total uguale a 0. Usa while let con .pop() per togliere i numeri dalla fine uno alla volta, aggiungere ciascuno a total, e stampare il numero e il nuovo totale. Suggerimento: total ha bisogno di mut, e total += n ci aggiunge un valore.
Mostra la soluzione
fn main() { let mut numbers = vec![4, 8, 15]; let mut total = 0;
while let Some(n) = numbers.pop() { total += n; println!("preso {n}, ora il totale è {total}"); }}Output:
preso 15, ora il totale è 15preso 8, ora il totale è 23preso 4, ora il totale è 27.pop() prende dalla fine, ed è per questo che il 15 esce per primo.
2. Controlla un’età scritta come testo
Sezione intitolata “2. Controlla un’età scritta come testo”Salva let input = "17";. Se si può convertire in un u32, stampa se quell’età è da maggiorenne (18 o più) o da minorenne. Se la conversione non riesce, stampa che non è un numero. Prova anche a cambiare input in "abc". Suggerimento: metti un normale if / else dentro il blocco if let.
Mostra la soluzione
fn main() { let input = "17";
if let Ok(age) = input.parse::<u32>() { if age >= 18 { println!("{age}: maggiorenne"); } else { println!("{age}: minorenne"); } } else { println!("'{input}' non è un numero"); }}Output:
17: minorenneCon input = "abc", la conversione fallisce, viene eseguito l’else esterno e il programma stampa 'abc' non è un numero.
Riepilogo
Sezione intitolata “Riepilogo”if let Pattern = valore { ... }esegue il blocco solo sevalorecorrisponde aPattern: tutto il resto viene saltato in silenzio.- Aggiungi
elseper gestire il caso che non corrisponde, come in un normaleif, e concatena più pattern conelse if let. while let Pattern = valore { ... }continua a girare finché il valore corrisponde, utile per svuotare una collezione con.pop().- Usa
if let/while letquando ti interessa un solo pattern; usamatchquando devi gestire più casi in modo distinto.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Cosa succede quando viene eseguito if let Some(value) = age e age è None?
2. Quando conviene usare match invece di if let?
3. In while let Some(top) = stack.pop() { ... }, quando si ferma il ciclo?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- Concise Control Flow with if let and let…else — The Rust Book (in inglese): l’introduzione ufficiale, che parla anche di let-else.
- if let — Rust by Example (in inglese): esempi eseguibili con
Optione con enum tuoi, e perchéif letbattematchper un solo pattern. - while let — Rust by Example (in inglese): un ciclo riscritto da
loop+matchin un più brevewhile let. if letpatterns — The Rust Reference (in inglese): le regole esatte, compreso il rapporto traif letematch.