Salta ai contenuti

Match in Rust

match confronta un valore con una lista di pattern ed esegue il codice di quello che corrisponde. Pensalo come una versione più potente dell’istruzione switch, se ne hai mai usata una; e se non l’hai mai fatto, resta comunque uno degli strumenti più utili da imparare presto.

fn main() {
let day = 3;
match day {
1 => println!("Lunedì"),
2 => println!("Martedì"),
3 => println!("Mercoledì"),
4 => println!("Giovedì"),
5 => println!("Venerdì"),
_ => println!("Fine settimana"),
}
}

Output:

Mercoledì
▶ Provalo tu

Il _ è un jolly (catch-all): corrisponde a qualsiasi cosa non elencata sopra. match richiede che ogni valore possibile sia gestito, quindi di solito c’è _ a coprire «tutto il resto».

Puoi raggruppare più valori in un unico ramo usando |:

fn main() {
let day = 6;
match day {
1 | 2 | 3 | 4 | 5 => println!("Giorno feriale"),
6 | 7 => println!("Fine settimana"),
_ => println!("Giorno non valido"),
}
}
▶ Provalo tu

Per i numeri puoi far corrispondere un intero intervallo con ..=:

fn main() {
let score = 85;
match score {
90..=100 => println!("A"),
75..=89 => println!("B"),
60..=74 => println!("C"),
_ => println!("F"),
}
}
▶ Provalo tu

Proprio come if, anche match può produrre un valore:

fn main() {
let day = 6;
let kind = match day {
1 | 2 | 3 | 4 | 5 => "feriale",
6 | 7 => "fine settimana",
_ => "sconosciuto",
};
println!("{kind}");
}
▶ Provalo tu

match non serve solo per i numeri. Funziona anche con le slice di stringa (&str) e con i caratteri, e gli intervalli di caratteri come 'a'..='z' funzionano proprio come quelli numerici:

fn main() {
let command = "ferma";
match command {
"avvia" => println!("Avvio..."),
"ferma" | "esci" => println!("Mi fermo."),
_ => println!("Comando sconosciuto: {command}"),
}
let letter = 'k';
let kind = match letter {
'a' | 'e' | 'i' | 'o' | 'u' => "vocale",
'a'..='z' => "consonante",
'0'..='9' => "cifra",
_ => "qualcos'altro",
};
println!("'{letter}' è una {kind}");
let name = String::from("Ferris");
match name.as_str() {
"Ferris" => println!("Ciao, granchio!"),
_ => println!("Ciao, {name}!"),
}
}

Output:

Mi fermo.
'k' è una consonante
Ciao, granchio!
▶ Provalo tu

Due cose da notare:

  • L’ordine conta. Anche 'a' sta dentro 'a'..='z', ma il ramo delle vocali viene prima, quindi le vocali non arrivano mai al ramo delle consonanti. Rust sceglie sempre il primo ramo che corrisponde.
  • Una String ha bisogno di .as_str(). Il testo scritto tra virgolette, come "Ferris", è un &str. Se fai il match di una String direttamente contro di esso, Rust si ferma con error[E0308]: mismatched types ... expected String, found &str (tipi non corrispondenti: si aspettava una String e ha trovato un &str). Chiamare .as_str() ti dà una vista &str della String con cui i pattern possono confrontarsi. (Vedi la pagina Stringhe per la differenza tra i due.)

Il confronto distingue maiuscole e minuscole: "Ferma" non corrisponderebbe a "ferma". Se il testo arriva da un utente, un trucco comune è fare il match su input.to_lowercase().as_str().

Per i semplici controlli vero/falso vanno bene entrambi, ma match comincia a vincere quando hai più di due o tre possibilità: si legge meglio di una lunga catena di else if, e il compilatore controlla che tu abbia coperto ogni caso. if/else resta lo strumento giusto per una singola condizione sì/no.

Salva il numero di un mese, month (2). Usa match come espressione per calcolare quanti giorni ha (ignora gli anni bisestili), poi stampa Il mese 2 ha 28 giorni. Suggerimento: i mesi 1, 3, 5, 7, 8, 10 e 12 hanno 31 giorni; raggruppali in un unico ramo con |.

Mostra la soluzione
fn main() {
let month = 2;
let days = match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 => 28,
_ => 0, // non è un mese vero
};
println!("Il mese {month} ha {days} giorni");
}

Output:

Il mese 2 ha 28 giorni
▶ Provalo tu

I server web rispondono con un numero. Trasforma un code in un breve significato: 200 è "OK", 404 è "Non trovato", qualsiasi altro tra 400 e 499 è "Errore del client", tra 500 e 599 è "Errore del server", e tutto il resto è "Sconosciuto". Provalo con 503. Suggerimento: metti il ramo esatto 404 sopra l’intervallo 400..=499, altrimenti non verrà mai raggiunto.

Mostra la soluzione
fn main() {
let code = 503;
let meaning = match code {
200 => "OK",
404 => "Non trovato", // deve venire prima dell'intervallo 400..=499
400..=499 => "Errore del client",
500..=599 => "Errore del server",
_ => "Sconosciuto",
};
println!("{code}: {meaning}");
}

Output:

503: Errore del server
▶ Provalo tu
  • match confronta un valore con dei pattern ed esegue il ramo che corrisponde.
  • Ogni valore possibile deve essere gestito: usa _ come jolly.
  • Combina più valori con |, o fai corrispondere un intervallo con ..=.
  • Puoi fare il match anche di testo &str e di valori char; usa .as_str() per una String.
  • match, come if, si può usare come espressione per produrre un valore.

Verifica veloce

1. Cosa fa il pattern _ in un'espressione match?

2. Cosa succede se un'espressione match non gestisce ogni valore possibile e non ha un ramo _?

3. Come fai corrispondere più valori in un unico ramo, come 1, 2 o 3?

Punteggio: 0 / 3