Обработка ошибок в Rust
2021-12-21 19:04Rust на первый взгляд не особо отличается от Си++. Тоже классы и методы, ничего особенного. Однако подход ко многим вещам отличается. К примеру, обработка особых ситуаций и ошибок делается совсем по другому. Здесь я составил короткий обзор со ссылками на документацию.
Предыдущие примеры сокращаются до:
Для Option<T>, обычно нужно выделить значение из Some(value), а если встретится None - завершиться с ошибкой или выдать альтернативное значение.
Тип Option
Что, если значение может отсутствать? К примеру, функция может вернуть значение, если оно вычислено успешно, и вернуть что-то другое, если не получилось. Для таких случаев существует тип Option. Это энум с двумя вариантами:Если значение val получилось вычислить, возвращаем Some(val). Иначе возвращаем None. Пример такой функции - сложение двух чисел с обнаружением переполнения:pub enum Option<T> {
None,
Some(T),
}
Здесь x получит значение Some(690), поскольку переполнения не было.let x = 123.checked_add(567);
Тип Result
Часто при возникновении особых ситуаций нужно вернуть дополнительную информацию, уточняющую причину ошибки. Просто None недостаточно. Для этого существует другой тип: Result. Это тоже энум с двумя вариантами:В отличие от Option, в случае особой ситуации вместо None используется вариант Err(x), где значение x содержит код ошибки. Большинство функций стандартной библиотеки возвращает Result.enum Result<T, E> {
Ok(T),
Err(E),
}
Тип Error
В энуме Result<T,E> тип E может быть каким угодно, но в стандартной библиотеке принято давать ему интерфейс (трейт) std::error::Error. У трейта Error есть несколько малополезных методов низкого уровня, но главное - его можно напечатать через print!(), и получить объяснение ошибки.Оператор match
Результат типа Option или Result обычно обрабатывается с помощью оператора match. Пример для Option:Пример для Result:let val = match 123.checked_add(567) {
Some(x) => x,
None => return None,
};
let result = File::open("hello.txt");
let file = match result {
Ok(x) => x,
Err(e) => return Err(e),
};
Оператор '?'
Вышеприведённые фрагменты обработки значений типа Result или Option настолько часто встречаются, что для удобства был придуман способ их упростить. Если после выражения поставить вопросительный знак, значение будет обработано посредством match, ровно как написано выше. То есть в случае None (для Option) или Err (для Result) произойдёт преждевременный возврат из текущей функции.Предыдущие примеры сокращаются до:
Заметьте, что чтобы вопросительный знак правильно работал, текущая функция должна быть объявлена как возвращающая значение Option<T> или Result<T, E> соответственно.let val = 123.checked_add(567)?;
let file = File::open("hello.txt")?;
Выделение значений
Для типовой обработки значений типа Option или Result есть набор полезных методов.Для Option<T>, обычно нужно выделить значение из Some(value), а если встретится None - завершиться с ошибкой или выдать альтернативное значение.
- option.unwrap() - если None, вызвать panic!()
- option.expect(msg) - если None, вызвать panic!() с указанным сообщением об ошибке
- option.unwrap_or(val) - если None, выдать указанное значение
- option.unwrap_or_default() - если None, выдать дефолтное значение для типа T
- option.unwrap_or_else(func) - если None, вызвать функцию и вернуть её значение
- result.unwrap() - если Err, вызвать panic!()
- result.expect(msg) - если Err, вызвать panic!() с указанным сообщением об ошибке
- result.unwrap_or(val) - если Err, выдать указанное значение
- result.unwrap_or_default() - если Err, выдать дефолтное значение для типа T
- result.unwrap_or_else(func) - если Err, вызвать функцию и вернуть её значение
- result.unwrap_err() - если Ok, вызвать panic!()
- result.expect_err(msg) - если Ok, вызвать panic!() с указанным сообщением об ошибке
Преобразование Result ⟷ Option
Можно преобразовать значение типа Result в Option:- result.ok() - преобразовать Ok(v) в Some(v), а Err(e) в None
- result.err() - преобразовать Err(e) to Some(e), а Ok(v) в None
- option.ok_or(err) - преобразовать Some(v) в Ok(v), а None в Err(err)
- option.ok_or_else(func) - преобразовать Some(v) to Ok(v), а None в Err(func())
Здесь x получит значение 590. Если бы случилось переполнение, программа завершилась бы с сообщением:let x = 123.checked_add(567).unwrap();
called `Option::unwrap()` on a `None` value