Rust - from Mozilla

Cicli di controllo            
 

Rust presenta le solite istruzioni per controllare il flusso del programma potendolo modificare in presenza di date condizioni. 
La prima è la classica sequenza condizionale che prevede la presenza delle keywords if - else. Non c'è nulla di particolare rispetto ad altri linguaggi, in queste cose non c'è molto spazio in genere per la fantasia. E' possibile trovare questo tipo di controllo nei seguenti tre formati:

if condizione booleana { istruzioni }

oppure

if espressione booleana { istruzioni }
else { istruzioni }

e infine

if { istruzioni }
else if condizione booleana{ istruzioni }
else if condizione booleana{ .... }
else { istruzioni }

Lo vediamo con un semplice esempio, che sarà completamente comprensibile più avanti

  Esempio 3.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main()
{
io::print("Inserisci un numero: ");
let reader = io::stdin();
let line = reader.read_line();
let mut n1 = 0;
match int::from_str(line) {
Some(x) => n1 = x,
None => {io::println(fmt!("Valore inserito scorretto - si pone valore = 0")); }
}
if n1 == 0
{
  io::println(fmt!("Hai inserito il numero 0"));
}
else if n1 > 0
{
  io::println(fmt!("Hai inserito un numero maggiore di zero"));
}
else
{
  io::println(fmt!("Hai inserito un numero minore di zero"));
}
}

La parte che ci interessa iniza alla riga 11 e termina alla 22 con la chiusura dell'ultimo else.

L'istruzione while è anch'essa uguale a ciò che si incontra in altri linguaggi:

while condizione booleana = true
{
istruzioni
}

ed ecco l'esempio:

  Esempio 3.2
1
2
3
4
5
6
7
8
9
fn main()
{
  let mut x = 0;
  while x < 5
  {
    println(int::to_str(x));
    x = x + 1;
  }
}

Il ciclo viene eseguito se e fintantoche la condizione booleana è verificata, nel caso dell'esempio 3.2 finchè x è minore di 5.

Il classico ciclo for è presente nella seguente forma:

for_expr : "for" expr [ '|' ident_list '|' ] ? '{' block '}' ;

Esempio:

  Esempio 3.3
1
2
3
4
5
6
7
fn main()
{
  for int::range(0, 10) |x|
  {
    println(int::to_str(x));
  }
}

In questo caso vengono stampati i numeri da 0 a 9, escluso quindi l'ultimo, il 10. Se aveste voglia di effettuare il loop modificando il passo potete ricorrere a range_step che accetta 3 parametri, il punto di partenza, quello di arrivo e, appunto il passo, come nel frammento di codice seguente:

for int::range_step(0, 10, 2) |x|

Per iterare da un numero più alto ad uno più basso si deve invece ricorrere a range_rev

for int::range_rev(20,10) |x|

Per andare all'indietro con un passo diverso da 1 si lavora ancora con range_step:

for int::range_step(20, 10, -2)

Rust fornisce anche l'interessante istruzione loop per dare la possibilità di creare un ciclo infinito. Si tratta di qualche cosa più elegante delle forzature che troviamo in altri linguaggi. Inoltre, in qualche occasione, può anche essere un'alternativa ai normali procedimenti per effettuare operazioni iterate. Per realizzare questo scopo interviene anche un'altra istruzione comune, con lo stesso uso in altri linguaggi, ovvero break. Vediamo un rapido esempio:

  Esempio 3.4
1
2
3
4
5
6
7
8
9
10
11
12
13
fn main()
{
  let mut x = 0;
  loop
  {
    io::println(int::to_str(x));
    x = x + 1;
    if x == 10
    {
      break
    }
  }
}

Senza le istruzioni dalla riga 8 alla 11 il programma non terminerebbe mai fino ad un overflow. Il codice dell'esempio 3.4 stampa i numeri da 0 a 9 poi il ciclo viene interrotto e il programma prosegue terminando. Come è evidente avendo ben chiaro il punto di fine è possibile usare loop per le nostre iterazioni.

Infine diamo uno sguardo della versione Rust dell'istruzione case o switch presente in altri linguaggi, ovvero match. Il suo uso è, come sempre, rivolto alla immediatezza d'uso. Essa permette di eseguire un branch, escludendo gli anni, sulla base del valore di una variabile. L'esempio seguente mostra la sintassi base, in qualche modo l'abbiamo già incontrata nell'esempio 3.1:

match x {
0 => io::println("zero"),
1 | 2 => io::println("one or two"),
3 | 10 => io::println("three to ten"),
_ => io::println("something else"),
}

come si vede i vari branch sono caratterizzati dal simbolo => che li introduce. Alcune considerazioni:

  • Non esiste il problema del fall-through ovvero il rischio di cadere da un branch in quello successivo; questo è evitato di default quindi se viene eseguito un ramo sono esclusi dall'esecuzione tutti gli altri.
  • Il simbolo _ (underscore) introduce il ramo che rastrella tutto quanto non è compreso nelle casistiche precedenti,
  • Nel caso vi siano più istruzioni in un certo ramo si può ricorrere a questa sintassi:  => { .... }
  • Il simbolo | che vedete sta per "or", è un operatore logico di cui riparleremo

PDel pattern matchin torneremo comunque a parlare.