Ciclo While in Rust
Il ciclo while in Rust ripete un blocco di codice finché una condizione booleana rimane vera. È la scelta naturale quando il numero di iterazioni non è noto in anticipo ma dipende da una condizione che viene valutata a ogni iterazione. Rust offre anche while let, una variante che combina il ciclo con il pattern matching.
While di base
La forma base del while valuta la condizione prima di ogni iterazione. Se la condizione è falsa fin dall’inizio, il corpo del ciclo non viene mai eseguito.
fn main() {
let mut contatore = 0;
while contatore < 5 {
println!("Contatore: {contatore}");
contatore += 1;
}
println!("Ciclo terminato, contatore finale: {contatore}");
}
Output:
Contatore: 0
Contatore: 1
Contatore: 2
Contatore: 3
Contatore: 4
Ciclo terminato, contatore finale: 5
Come per l’if, la condizione deve essere di tipo bool. Non servono parentesi tonde attorno alla condizione.
Countdown con while
Un classico esempio è il conto alla rovescia:
fn main() {
let mut secondi = 10;
while secondi > 0 {
println!("{secondi}...");
secondi -= 1;
}
println!("Partenza!");
}
While con condizioni complesse
È possibile combinare più condizioni con gli operatori logici.
fn main() {
let mut x = 0;
let mut somma = 0;
while x < 100 && somma < 500 {
somma += x;
x += 7;
}
println!("x = {x}, somma = {somma}");
}
While let (pattern matching nel ciclo)
while let è una forma speciale che continua il ciclo finché un pattern viene soddisfatto. È particolarmente utile con Option e Result.
fn main() {
let mut stack = vec![1, 2, 3, 4, 5];
while let Some(valore) = stack.pop() {
println!("Estratto: {valore}");
}
println!("Lo stack è vuoto");
}
In questo esempio, stack.pop() restituisce Some(valore) finché ci sono elementi e None quando il vettore è vuoto. Il ciclo continua finché il pattern Some(valore) corrisponde.
Un altro uso comune è con gli iteratori:
fn main() {
let numeri = vec![10, 20, 30, 40];
let mut iter = numeri.iter();
while let Some(n) = iter.next() {
println!("Valore: {n}");
}
}
While let con enum personalizzate
while let funziona con qualsiasi tipo che supporta il pattern matching:
enum Comando {
Stampa(String),
Somma(i32, i32),
Esci,
}
fn main() {
let mut comandi = vec![
Comando::Stampa(String::from("Ciao")),
Comando::Somma(3, 4),
Comando::Stampa(String::from("Mondo")),
];
comandi.reverse();
while let Some(cmd) = comandi.pop() {
match cmd {
Comando::Stampa(testo) => println!("{testo}"),
Comando::Somma(a, b) => println!("{a} + {b} = {}", a + b),
Comando::Esci => break,
}
}
}
Pattern comuni
Leggere input fino a una condizione
use std::io;
fn main() {
let mut input = String::new();
while input.trim() != "esci" {
input.clear();
println!("Scrivi qualcosa (o 'esci' per terminare):");
io::stdin().read_line(&mut input).expect("Errore di lettura");
println!("Hai scritto: {}", input.trim());
}
}
Ricerca con while
fn main() {
let dati = [4, 8, 15, 16, 23, 42];
let obiettivo = 16;
let mut indice = 0;
while indice < dati.len() {
if dati[indice] == obiettivo {
println!("Trovato {obiettivo} all'indice {indice}");
break;
}
indice += 1;
}
}
Accumulatore
fn main() {
let mut n = 1;
let mut fattoriale: u64 = 1;
while n <= 20 {
fattoriale *= n;
println!("{n}! = {fattoriale}");
n += 1;
}
}
Differenze tra while e loop
In Rust, while e loop servono scopi diversi:
loop: ciclo infinito, esplicitamente interrotto conbreak. Può restituire valori.while: ciclo con condizione verificata a ogni iterazione. Non restituisce valori (il tipo di ritorno è sempre()).
Se hai bisogno di un ciclo che restituisca un valore, usa loop con break valore.
Conclusione
Il ciclo while è ideale quando la condizione di terminazione è nota e verificabile a ogni iterazione. La variante while let aggiunge la potenza del pattern matching, rendendo il codice più idiomatico quando si lavora con tipi come Option e Result. Per iterare su collezioni con indice noto, considera l’uso del ciclo for, che in Rust è generalmente preferito per la sua chiarezza e sicurezza.