Rust - Il ciclo loop
Il ciclo loop in Rust è il costrutto più fondamentale per creare iterazioni. La sua caratteristica distintiva è che non ha una condizione di uscita implicita: esegue il corpo all’infinito, finché non interviene un break, al verificarsi di una certa condizione o non interviene un qualche intervento di arresto, ad esempio via tastiera da parte dell'utente. La sintassi di base è molto semplice:
loop {
istruzioni
}
L'esempio è semplice:
Esempio 20.1
fn main() {
loop {
println!("ciao!");
}
}
Questo programma, come è facile verificare stampa all'infinito la stringa "ciao" finche non viene interrotto con gli opportuni comandi da tastiera. All'interno di un ciclo loop possiamo ovviamente usare i consueti break e continue:
Esempio 20.2
fn main(){
let mut x = 0;
loop {
x = x + 1;
if x == 5 {
continue;
}
print!("X = {} \n ", x);
if x == 8 {
break;
}
}
}
da cui otteniamo:
| X = 1 X = 2 X = 3 X = 4 X = 6 X = 7 X = 8 |
saltando il 5 e terminando quando viene raggiunto il numero 8.
Di per sè stesso, il nostro loop come espressione produce il solito ( ) che serve a poco. Tuttavia in congiunzione con break possiamo usarlo anche in modo più interattivo con il resto dell'ambiente.
Esempio 20.3
fn main(){
let mut x = 0;
let y = loop {
x = x + 1;
if x == 7 {
break x * 3
};
};
print!("Y = {} \n", y);
}
da cui otteniamo:
| Y = 21 |
In questo caso quindi abbiamo avuto un esito che abbiamo abbinato alla variabili y. Volendo si può usare proprio come espressione diretta:
Esempio 20.4
fn main(){
let y = loop {
break 10
};
print!("Y = {} \n", y);
}
Inutile dire qual è l'output.
Curioso notare come loop sia talmente
"gradito" al linguaggio per creare cicli potenzialmente infiniti che
se provate
a sostituirlo con while true
per
farne le veci, il compilatore emette un warning che inizia con:
warning: denote infinite loops with `loop { ... }`
Una cosa interessante nasce dalla considerazione che Rust non ha il costrutto do...while presente in altri linguaggi (o anche il repat..until del Pascal che fa la stessa cosa) che esegue almeno una iterazione prima di valutare la condizione. Si può usare loop per simulare quella sequenza inserendo la condizione e il break.
L'istruzione loop non è strettamente
necessaria in sè stessa, si può magari ricorrere a vari artifici per
raggiungere lo stesso scopo da esso prodotto, ma qualche vantaggio non
secondario lo offre:
1) Semplicità: La sintassi di
loop è estremamente semplice e chiara. Non
c'è bisogno di definire una condizione booleana all'inizio; il ciclo
continua a eseguire fino a quando non viene incontrato un comando di
controllo del flusso o una interruzione diversa.
2)
Garanzia di esecuzione, come abbiamo visto: a differenza di
while, che potrebbe non eseguire mai il suo
corpo se la condizione iniziale è falsa, un loop
eseguirà sempre il suo corpo almeno una volta, a meno che non ci sia un
break o un return
prima di qualsiasi altra istruzione all'interno (il che avrebbe poco senso).
Un errore comune è quello di sbagliare la condizione di arresto, se ci deve
essere.
3) Uso con break
per restituire valori: Rust permette di usare
break con un valore per terminare un loop
e restituire quel valore dal ciclo. Questo può essere molto utile per
situazioni in cui si vuole restituire un risultato calcolato iterativamente.
4) Costruzione ideale per polling o cicli di attesa:
loop è ideale per situazioni come il
polling, dove è necessario eseguire un'azione ripetutamente fino a che una
condizione esterna è soddisfatta e non si sa a priori quante iterazioni
saranno necessarie. Ma viene usato favorevolmente anche per scroivere
macchine a stati finiti, algoritmi operativi di varia natura.
5)
Facilità di gestione dell'uscita: gestire l'uscita da un
loop può essere più diretto poiché l'unico
punto di uscita è esplicitamente marcato con break,
rendendo il flusso del programma più chiaro e meno propenso a errori
accidentali di logica.
Per cui usatelo senza timore quando pensate vi
possa essere utile.