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.