Rust построен на явном управлении ошибками: исключения отсутствуют, вместо этого используется возвращаемое значение Result<T, E>. Это обеспечивает безопасность и предсказуемость кода.
История вопроса:
Многие языки шли по пути исключений, что приводило к неожиданным ситуациям и необходимости явно обрабатывать исключения в runtime. Rust с самого начала сделал ставку на контроль через типы, все ошибки должны быть частью сигнатуры функции.
Проблема:
Основная задача — писать код, в котором ошибки не проигнорированы и не замаскированы, нет "падающих" функций, но при этом код остаётся компактным и читабельным. Необходимо корректно прокидывать ошибки выше, не теряя информации об их типе, и не запутывать логику.
Решение:
Ключевой инструмент — тип Result<T, E> и оператор ?, автоматически "разворачивающий" результат: при ошибке происходит немедленный выход из функции с возвратом ошибки, а при успехе возвращается значение.
Пример кода:
fn read_number(file: &str) -> Result<i32, std::io::Error> { let content = std::fs::read_to_string(file)?; let num: i32 = content.trim().parse()?; Ok(num) }
Ключевые особенности:
.map_err() и другие методы)Можно ли использовать ? в функциях, возвращающих Unit (void)?
Нет, оператор ? допустим только внутри функций, возвращающих Result или Option. Если функция возвращает (), ? использовать нельзя.
Что произойдёт, если типы ошибок в нескольких вызовах с ? разные?
Получается ошибка компиляции: тип ошибки должен быть однозначно определён. Нужно либо привести все ошибки к единому типу через .map_err() или использовать thiserror, либо описывать enum-обёртку на уровне API. Пример:
fn foo() -> Result<_, MyError> { let a = bar()?; let b = baz().map_err(MyError::Baz)?; Ok(…) }
Чем опасен .unwrap() во внутренней логике?
Распространено ошибочное мнение, что в "основном" коде можно смело использовать unwrap(), "оно точно не упадёт". На деле даже маленькая невидимая ошибка приведёт к панике времени выполнения — нарушится безопасность программы.
unwrap/expect, особенно во внутренней логикеВ продакшен-коде оставили много unwrap() после быстрой отладки. В итоге приложение падало при любом сбое парсинга из-за некорректного ввода пользователя.
Плюсы:
Минусы:
Использовали полный стек Result<T, E> с эксплицитным описанием ошибок и применяли только ? и .map_err(), сохраняя всю информацию о сбое.
Плюсы:
Минусы: