Użycie typu Result stało się jednym z kluczowych podejść do obsługi błędów w Rust. Historycznie w wielu językach— na przykład w C— błędy często były sygnalizowane przez specjalne wartości zwrotne lub zmienne globalne, co prowadziło do częstych błędów przy ignorowaniu tych sygnałów. Rust poszedł w kierunku jawnej typizacji błędów za pomocą enum Result<T, E>, co sprawia, że niemożliwe jest przypadkowe zignorowanie błędu — kompilator wymusza obsługę obu gałęzi (sukcesu i niepowodzenia).
Problem: Należało uczynić obsługę błędów jak najbardziej bezpieczną i czytelną, wyeliminować "ukryte" błędy oraz zwiększyć niezawodność kodu bez konieczności stosowania wyjątków.
Rozwiązanie: Result<T, E> to wyliczenie z dwoma wariantami: Ok(T) w przypadku sukcesu i Err(E) w przypadku błędu. To zmusza do jawnej obsługi błędów, lub jawnego ich ignorowania za pomocą unwrap lub oczekiwania na paniki. Ponadto operator ? sprawia, że powszechne wzorce przekazywania błędów są zwięzłe.
Przykład kodu:
use std::fs::File; use std::io::{self, Read}; fn read_file(path: &str) -> Result<String, io::Error> { let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents) }
Kluczowe cechy:
?.Czy zawsze można używać operatora ? do automatycznego przekazywania błędu w górę?
Nie, tylko jeśli typ błędu funkcji zgadza się z typem wyrażenia po prawej stronie ?. Jeśli typy są niezgodne, należy wykonać jawne przekształcenie błędu za pomocą metody .map_err() lub własnego typu.
Przykład kodu:
fn f() -> Result<(), String> { let _f = File::open("foo").map_err(|e| e.to_string())?; Ok(()) }
Czy można pozostawić wynik Result nieużywanym, jeśli po prostu nie interesują cię błędy?
Nie, kompilator wyda ostrzeżenie lub błąd, jeśli wynik typu Result nie zostanie obsłużony. Albo wywołujesz .unwrap(), albo jawnie wywołujesz .ok()/.err()/let _ = ... lub poprawnie logujesz błąd.
Co się stanie, jeśli wywołasz .unwrap() na Result z błędem?
Zostanie wywołany panic! i wykonanie programu zostanie przerwane, co zazwyczaj prowadzi do awaryjnego zakończenia. Dlatego unwrap() jest dozwolone tylko wtedy, gdy sukces jest gwarantowany.
let _ = ...;) bez logowaniaProgramista postanowił odczytać konfigurację z pliku i użył unwrap() na wyniku odczytu.
Zalety:
Wady:
Błędy podczas odczytu konfiguracji są logowane i zwracane w górę po stosie wywołań za pomocą Result + operator ?.
Zalety:
Wady: