W Rust nie ma tradycyjnych wyjątków (exceptions) jak w Javie czy C++; zamiast tego używane są typy opakowujące Result i Option do zwracania błędów.
Result<T, E> — przechowuje albo wartość (Ok(T)), albo błąd (Err(E)). Zwykle stosuje się go do operacji, które mogą nie zostać poprawnie wykonane (na przykład wejście/wyjście plikowe).
Option<T> — dla wartości opcjonalnych: albo Some(T), albo None, bez informacji o błędzie. Przykład:
fn divide(x: f64, y: f64) -> Option<f64> { if y == 0.0 { None } else { Some(x / y) } }
W Rust nie ma wyjątków w stylu try/catch, aby zapewnić przejrzysty, kontrolowany przepływ błędów i nie wymagać zbieracza śmieci ani mechanizmu unwind do utrzymania wyjątków.
Dlaczego panic! nie można używać do zwykłej obsługi błędów?
Odpowiedź: panic! kończy strumień wykonania, nie daje możliwości odzyskania, prowadzi do unwinding/abort. Jeśli błędy są zwracane przez Result, strona wywołująca może je obsłużyć. Przykład:
fn foo() -> Result<(), String> { Err("Coś poszło nie tak".to_string()) } // zamiast fn foo() { panic!("Coś poszło nie tak"); }
Historia
W mikrousługach do przetwarzania obrazów deweloper użył
panic!do obsługi wszystkich nieprzewidzianych sytuacji. Prowadziło to do awaryjnego zakończenia usługi przy każdej błędzie zamiast zwrócenia poprawnej odpowiedzi HTTP klientowi z szczegółami błędu.
Historia
W jednym narzędziu CLI użyto
unwrap()do całego wejścia/wyjścia, nie obsługując możliwych błędów. W rezultacie w przypadku błędu systemu plików program po prostu kończył działanie bez jakiegokolwiek komunikatu lub diagnostyki.
Historia
W usłudze analitycznej próbowano użyć Option dla każdego najmniejszego błędu, pozbawiając się informacji o samych błędach, co utrudniało debugowanie. Po przejściu na Result z błędami w enumach łatwiej było znajdować i naprawiać błędy.