Storia della domanda
L'operatore match in Rust è uno strumento potente per la corrispondenza dei modelli, preso in prestito dai linguaggi funzionali. A differenza degli equivalenti in C/C++, in Rust viene effettuato un rigoroso controllo di esaustività (exhaustiveness checking), e per ogni possibile variante devono esserci i casi del ramo.
Problema
Gli errori durante l'uso di match sono spesso legati a un'errata considerazione di tutte le varianti di tipo (ad esempio, enum), a un'errata gestione delle guardie (condizioni sui rami) o a una complessa struttura annidata. Un'elaborazione errata porta a errori in fase di compilazione o a logica implicitamente errata.
Soluzione
_).if dopo il pattern).Esempio di codice:
enum Shape { Circle(f64), Rectangle { width: f64, height: f64 }, } fn print_area(s: Shape) { match s { Shape::Circle(r) if r > 0.0 => println!("area = {}", 3.14 * r * r), Shape::Rectangle { width, height } if width > 0.0 && height > 0.0 => println!("area = {}", width * height), _ => println!("invalid shape"), } }
Caratteristiche chiave:
Influisce l'ordine dei rami in match sull'esecuzione?
Sì, l'ordine dei rami è importante: non appena viene trovata la prima corrispondenza, le successive non vengono controllate. Questo è particolarmente critico con pattern guard — se prima si trova un ramo con guard, cattura il valore prima del catch-all.
È obbligatorio il catch-all (_) quando si matcha su enum?
No, se hai effettivamente esaminato tutti i casi (e i varianti di dichiarazione del tipo non cambieranno nel tempo). Tuttavia, il catch-all è necessario quando si lavora con tipi che possono ricevere valori aggiuntivi o quando non si desidera elaborare esplicitamente tutti i rami.
È possibile utilizzare più pattern (alternative) in un ramo match?
Sì. Tramite la barra verticale (|) è possibile combinare i pattern:
match x { 1 | 2 | 3 => println!("one, two or three"), _ => println!("something else"), }
_ troppo presto (prima dei rami specifici)Uno sviluppatore ha scritto un match con catch-all all'inizio e non è riuscito a gestire correttamente i casi specifici.
Vantaggi:
Il programma si compila.
Svantaggi:
La logica specifica non funziona mai, parte del codice non è coperta.
Elaborazione esplicita di tutte le varianti di enum, catch-all solo come ultimo ramo, guardie separate per i casi atipici.
Vantaggi:
Prevedibilità, il compilatore aiuta a non dimenticare una variante, facile da espandere i tipi.
Svantaggi:
Codice boilerplate aggiuntivo con un numero elevato di varianti.