Rust - from Mozilla

Pattern matching            

In questo paragrafo parliamo di un'istruzione molto comunemente usata per effettuare una selezione, specialmente laddove vi siano molte alternative in vece di lunghe catene di if, ma anche un'assegnazione,  Nel primo modo di utilizzo è simile, come logica, all'istruzione switch che incontriamo in molti altri linguaggi, come C++ o C#. La sintassi è la seguente:

match variabile {
valore 1 => codice,
valore 2 => codice,
....
valore n => codice,
_ => tutto il resto,
}

match è la keyword che introduce il blocco. La variabile viene confrontata con i valori e il primo corrispondente a quello della variabile stessa sottende il branch, il ramo, che sarà eseguito. Se nessun valore coincide con quello della variabile allora viene eseguito il ramo che comporende le altre possibilità, introdotto da _ (underscore). Da notare la virgola che separa i vari casi. Vediamo un esempio di partenza:

  Esempio 6.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use std::io;
use std::io::prelude::*;
fn main()
{
  print!( "Inserisci un numero: ");
  io::stdout().flush().ok().expect("");
  let mut input01 = String::new();
  io::stdin().read_line(&mut input01);
  let x01: i32 = input01.trim().parse()
  .ok()
  .expect("Please type a number!");
  match x01 {
    1 => print!("Hai inserito il numero 1"),
    2 => print!("Hai inserito il numero 2"),
    3 => print!("Hai inserito il numero 3"),
    _ => print!("Hai inserito un numero diverso da 1,2 o 3"),
  }
}

Se l'output è condiviso tra più di un valore si può usare l'OR, ad esempio, potremmo modificare il pattern come segue:

match x01 {
1 | 2 | 3 => print!("Hai inserito uno dei prim i 3 numeri"),
_ => print!("Tutto il resto..."),
}

Se doveste inserire più istruzioni in uno stesso branch dovrete ricorrere alle parentesi graffe, ne esce fuori una sintassi forse un po' strana:

match x01 {
1 | 2 | 3 => {
               println!("Hai inserito uno dei primi 3 numeri");
               print!("Sei astuto...")
             },
_ => print!("Tutto il resto..."),
}

E' interessante notare cosa accade se non mettere il ramo introdotto proprio da _ omettendolo infatti il compilatore vi avviserà:

error: non-exhaustive patterns: `_` not covered [E0004]

Essendo x01 un intero il compilatore cerca di avvisarvi che non avete preso in esame tutti i possibili valori della vostra variabile.

Un'altra possibilità  di utilizzo è illustrata dal seguente esempio:

  Esempio 6.2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use std::io;
use std::io::prelude::*;
fn main()
{
  print!( "Inserisci un numero: ");
  io::stdout().flush().ok().expect("");
  let mut input01 = String::new();
  io::stdin().read_line(&mut input01);
  let x01: i32 = input01.trim().parse()
  .ok()
  .expect("Please type a number!");
  let numero = match x01
  {
    1 => "uno",
    2 => "due",
    3 => "tre",
    _ => "altro",
  };
  print!("{}", numero);
}

In pratica è possibile attribuire un valore ad un elemento, sia variabile che costante, utilizzando l'output del nostro match. Si noti il ; alla riga 18.

Vedremo altre utili applicazioni del matching quando affronteremo ad esempio tuple ed enums.