% Match
Spesso, un semplice if
/else
non basta, perché ci sono più di due
opzioni possibili. Inoltre, le condizioni possono diventare parecchio
complesse. Rust ha una parola-chiave, match
("combacia"), che consente
di sostituire dei complicati raggruppamenti di if
/else
con qualcosa di più potente. Ecco qua:
let x = 5;
match x {
1 => println!("uno"),
2 => println!("due"),
3 => println!("tre"),
4 => println!("quattro"),
5 => println!("cinque"),
_ => println!("qualcos'altro"),
}
match
prende un'espressione ed esegue una diramazione in base al suo valore.
Ogni ‘braccio’ della diramazione ha la forma valore => espressione
.
Quando il valore combacia, l'espressione di quel braccio viene valutata.
Viene chiamata match
a causa del concetto di ‘pattern matching’, di cui
match
è un'implementazione. C'è un separate section on patterns
che tratta di tutti i pattern che sono ammessi qui.
Uno dei molti vantaggi di match
è che impone la ‘verifica di esaustività’.
Per esempio, se si toglie l'ultimo braccio, quello con il carattere _
,
il compilatore darà l'errore:
error: non-exhaustive patterns: `_` not covered
Rust ci dice che abbiamo dimenticato qualche valore. Il compilatore inferisce
dall'x
che può avere qualunque valore a 32 bit, da -2.147.483.648
a 2.147.483.647. Il carattere _
agisce da 'prendi-tutto', e prenderà
tutti i possibili valori che non sono specificati in un braccio
dello stesso match
. Come si vede nell'esempio precedente, al match
vengono forniti bracci per gli interi da 1 a 5, se x
vale 6 o qualunque
altro valore, viene preso dal caso _
.
Il costrutto match
è anche un'espressione, il che significa che lo si può
usare al lato destro di un'istruzione let
o direttamente dove è ammessa
una espressione:
let x = 5;
let numero = match x {
1 => "uno",
2 => "due",
3 => "tre",
4 => "quattro",
5 => "cinque",
_ => "qualcos'altro",
};
Talvolta è un modo carino di convertire qualcosa da un tipo a un altro; in
questo esempio gli interi vengono convertiti in String
.
Un altro impiego importante della parola-chiave match
sta nell'elaborare
le possibili varianti di un'enumerazione:
enum Messaggio {
Abbandona,
CambiaColore(i32, i32, i32),
Sposta { x: i32, y: i32 },
Scrivi(String),
}
fn abbandona() { /* ... */ }
fn cambia_colore(r: i32, g: i32, b: i32) { /* ... */ }
fn sposta_cursore(x: i32, y: i32) { /* ... */ }
fn elabora_messaggio(msg: Messaggio) {
match msg {
Messaggio::Abbandona => abbandona(),
Messaggio::CambiaColore(r, g, b) => cambia_colorw(r, g, b),
Messaggio::Sposta { x: x, y: y } => sposta_cursore(x, y),
Messaggio::Scrivi(s) => println!("{}", s),
};
}
Ancora, il compilatore Rust verifica l'esaustività, e richiede di avere
un braccio combaciante per ogni variante dell'enum. Se ne manca qualcuno, darà
un errore di compilazione, a meno che si usi il braccio _
.
Diversamente dai precedenti utilizzi di match
, questo caso non è
sostituibile da un semplice uso del costrutto if
. Si può però usare
il costrutto if let
, che può essere visto come una forma
abbreviata di match
.