In Rust non ci sono le solite eccezioni come in Java o C++; invece si usano i tipi wrapper Result e Option per restituire errori.
Result<T, E> — contiene o un valore (Ok(T)), o un errore (Err(E)). Viene generalmente usato per operazioni che possono non riuscire correttamente (ad esempio, operazioni di input/output su file).
Option<T> — per valori opzionali: o Some(T), o None, senza informazioni sull'errore. Esempio:
fn divide(x: f64, y: f64) -> Option<f64> { if y == 0.0 { None } else { Some(x / y) } }
In Rust non ci sono eccezioni nello stile try/catch per garantire un flusso di errori trasparente e controllato e per non richiedere un garbage collector o un motore di unwind per il mantenimento delle eccezioni.
Perché panic! non può essere usato per la normale gestione degli errori?
Risposta: panic! termina il flusso di esecuzione, non permette di recuperare, porta a unwinding/abort. Se gli errori vengono restituiti tramite Result, la parte chiamante può gestirli. Esempio:
fn foo() -> Result<(), String> { Err("Qualcosa è andato storto".to_string()) } // invece di fn foo() { panic!("Qualcosa è andato storto"); }
Storia
In un microservizio per l'elaborazione delle immagini, uno sviluppatore ha usato
panic!per gestire tutte le situazioni anomale. Questo portava a una terminazione anomala del servizio in caso di errore invece di restituire una corretta risposta HTTP al client con i dettagli dell'errore.
Storia
In uno strumento CLI usavano
unwrap()per tutto l'input/output, senza gestire gli eventuali errori. Di conseguenza, in caso di errore del file system, il programma terminava semplicemente senza alcun messaggio o diagnostica.
Storia
In un servizio di analisi cercarono di utilizzare Option per ogni piccolo errore, perdendo informazioni sugli errori stessi, rendendo difficile il debug. Dopo il passaggio a Result con errori enum, è diventato più semplice trovare e correggere i bug.