Использование типа Result стало одним из ключевых подходов к обработке ошибок в Rust. Исторически во многих языках— например, C— ошибки часто сигнализировались специальными значениями возврата или глобальными переменными, что приводило к частым ошибкам при игнорировании этих сигналов. Rust пошёл по пути явной типизации ошибок с помощью enum Result<T, E>, что делает невозможным случайное игнорирование ошибки — компилятор заставляет обработать обе ветви (успех и неудача).
Проблема: Требовалось сделать обработку ошибок максимально безопасной и читаемой, исключить "скрытые" ошибки, а также повысить надёжность кода без необходимости использования исключений.
Решение: Result<T, E> — это перечисление с двумя вариантами: Ok(T) при успехе и Err(E) при ошибке. Это вынуждает явно обрабатывать ошибки, либо явно их игнорировать с помощью unwrap или ожидать panics. Кроме того, оператор ? делает распространённые паттерны передачи ошибок лаконичными.
Пример кода:
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) }
Ключевые особенности:
?.Всегда ли можно использовать оператор ? для автоматической передачи ошибки наверх?
Нет, только если тип ошибки функции совпадает с типом выражения справа от ?. Если типы несовместимы, нужно сделать явное преобразование ошибки с помощью метода .map_err() или своего типа.
Пример кода:
fn f() -> Result<(), String> { let _f = File::open("foo").map_err(|e| e.to_string())?; Ok(()) }
Можно ли оставить результат Result неиспользуемым, если просто не интересуетесь ошибками?
Нет, компилятор выдаст предупреждение или ошибку, если результат типа Result не обработан. Либо вызываете .unwrap(), либо явно вызываете .ok()/.err()/let _ = ... или правильно логируете ошибку.
Что произойдёт, если вызвать .unwrap() на Result с ошибкой?
Будет вызван panic! и выполнение программы прервётся, обычно это приводит к аварийному завершению. Поэтому unwrap() допустим только тогда, когда гарантирован успех.
let _ = ...;) без логированияРазработчик решил считать конфиг из файла и использовал unwrap() на результате чтения.
Плюсы:
Минусы:
Ошибки при чтении конфига логгируются и возвращаются наверх по стеку вызовов с помощью Result + оператор ?.
Плюсы:
Минусы: