Il pattern matching è uno dei meccanismi linguistici più importanti in Rust, proveniente dai linguaggi funzionali. Permette di analizzare in modo dichiarativo, conciso e sicuro le complesse varianti di valori, anche tramite condizioni aggiuntive (espressioni guard), offrendo flessibilità e controllo sulla logica.
Senza exhaustiveness checking (verifica dell'analisi esaustiva di tutte le varianti), parte della potenza del pattern matching può essere realizzata in modo errato. Inoltre, senza comprendere l'ordine dei rami e delle espressioni guard, si possono commettere errori sia nella logica sia nelle prestazioni.
In Rust, il compilatore verifica che tutte le varianti dell'enum (o strutture pattern più semplici) siano considerate, oppure che ci sia un ramo _. Il ramo può essere ulteriormente limitato da un'espressione guard (if dopo il pattern), e solo se la condizione è soddisfatta, esso "scatta". Le varianti rimanenti non vengono catturate. L'ordine dei rami è importante: vengono controllati dall'alto verso il basso.
Esempio di codice:
enum Message { Hello, Data(i32), Quit, } fn handle(msg: Message) -> &'static str { match msg { Data(n) if n > 10 => "Big Data", Data(_) => "Some Data", Hello => "Greet!", Quit => "Bye", } }
Caratteristiche chiave:
Scatta il ramo con guard se il pattern corrisponde, ma la condizione non viene soddisfatta?
No, in questo caso il controllo passa al prossimo ramo adatto. Pattern + guard è un "filtro" atomico; solo quando entrambi corrispondono, viene eseguito il corpo del ramo.
Influisce l'ordine dei rami in match sulle prestazioni?
Sì. Soprattutto con una moltitudine di pattern simili con guard: il compilatore controlla i rami dall'alto verso il basso, il che influenza la velocità di controllo a runtime — i valori più comuni dovrebbero essere trattati prima.
È possibile omettere l'exhaustiveness checking mettendo solo il ramo _?
Tecnicamente sì — è permesso, ma si perde affidabilità: se il tipo esclude (o aggiunge) nuovi elementi, il compilatore non avviserà di casi non considerati. È sempre meglio gestire esplicitamente i casi importanti, e "_" solo come ultima risorsa.
Codice match per enum con espressioni guard, dove il pattern con guard è ultimo, ma la maggior parte dei valori viene elaborata direttamente dal ramo _ precedente, e non raggiunge mai la necessaria elaborazione.
Pro:
_.Contro:
Inizialmente vengono elencate le varianti più comuni e importanti (con guard), poi si coprono esaustivamente le altre — senza codice superfluo in "_".
Pro:
Contro: