Определение
Тип Result
— это обобщенное перечисление из стандартной библиотеки Rust и результат вычисления: успешный с вариантом Ok
и неуспешный с Err
.
Тип Result
определяется так:
enum Result<T, E> {
Ok(T),
Err(E),
}
The T
и E
— это типы успешных и неуспешных результатов соответственно. Result<T, E>
применяется при вычислении, в котором возвращается значение типа T
при успехе и ошибка типа E
при неуспехе.
Пример использования
Вот Result
для оборачивания результата функции, которая может выполниться неуспешно:
fn parse_int(s: &str) -> Result<i32, std::num::ParseIntError> {
s.parse::<i32>().map_err(|e| e.into())
}
fn main() {
let result = parse_int("5");
match result {
Ok(n) => println!("Parsed integer: {}", n),
Err(e) => println!("Error parsing integer: {}", e),
}
}
Если попытка спарсить число из строки успешна, в функции parse_int
возвращается вариант Ok
с полученным числом, если неуспешна — вариант Err
с ParseIntError
.
Обработка значений Result
Для обработки значения Result
применяется сопоставление с образцом. В примере выше для обработки вариантов Ok
и Err
использовано выражение match
.
Кроме того, чтобы извлечь значение из варианта Ok
, применяется метод unwrap
. Но он «запаникует», если значение — вариант Err
:
let result = parse_int("5");
let n = result.unwrap(); // «запаникует», если результат — вариант Err
println!("Parsed integer: {}", n);
Из-за возможности паники unwrap()
не рекомендуется использовать в производственном коде.
Другой способ обработки значения Result
— методы map
и map_err
: значение внутри варианта Ok
и Err
преобразуется применением к нему функции.
В функции divide
, например, делятся 2 числа и возвращается Result
— показатель успешного деления:
fn divide(x: i32, y: i32) -> Result<i32, DivisionError> {
if y == 0 {
return Err(DivisionError::DivideByZero);
}
Ok(x / y)
}
map()
и map_err()
Чтобы преобразовать успешный результат деления в другой тип, например в число с плавающей точкой, применяется метод map
:
let result = divide(10, 2);
let f: Result<f32, DivisionError> = result.map(|n| n as f32);
Чтобы преобразовать значение ошибки в другой тип, применяется метод map_err
:
let result = divide(10, 0);
let f: Result<i32, &str> = result.map_err(|e| match e {
DivisionError::DivideByZero => "Divide by zero",
DivisionError::Negative => "Cannot divide by negative number",
});
Другие методы обработки результата
Кроме методов map
и map_err
, у типа Result
имеются и другие способы манипулирования значениями и их обработки:
fn add_one(x: i32) -> Result<i32, &'static str> {
if x > 100 {
return Err("Number too large");
}
Ok(x + 1)
}
fn add_two(x: i32) -> Result<i32, &'static str> {
if x > 50 {
return Err("Number too large");
}
Ok(x + 2)
}
fn add_three(x: i32) -> Result<i32, &'static str> {
if x > 30 {
return Err("Number too large");
}
Ok(x + 3)
}
// С использованием and_then
let result = add_one(5).and_then(|x| add_two(x)).and_then(|x| add_three(x));
assert_eq!(result, Ok(11));
// С использованием or_else
let result = add_one(105).or_else(|e| add_two(5)).or_else(|e| add_three(5));
assert_eq!(result, Ok(7));
// С использованием unwrap_or
let result = add_one(105).unwrap_or(100);
assert_eq!(result, 100);
// С использованием unwrap_or_else
let result = add_one(105).unwrap_or_else(|e| -1);
assert_eq!(result, -1);
В этих примерах показаны 3 функции, каждая из которых выполняет операцию и возвращает значение Result
.
Методом and_then
они связываются, и, если все вычисления успешные, возвращается конечный результат операций. Если какое-либо вычисление неуспешно, сразу возвращается вариант Err
.
Метод or_else
аналогичен, но применяется к варианту Err
, а не Ok
. Здесь также указывается резервное вычисление, если исходное неуспешно.
В методе unwrap_or
возвращается значение внутри варианта Ok
или значение по умолчанию, если Result
— вариант Err
. Метод пригодится, когда в случае ошибки нужно указать значение по умолчанию, а не обрабатывать ее явно.
Метод unwrap_or_else
аналогичный, но, чтобы получить значение по умолчанию, в качестве аргумента принимается замыкание, применяемое к значению ошибки внутри варианта Err
. Метод пригодится, когда нужно вычислить значение по умолчанию на основе значения ошибки.
Читайте также:
- Почему разработчики JavaScript используют инструменты на Rust
- Реализация Redux на Rust
- Actix или Rocket? Сравнение двух мощных платформ для веб-приложений на Rust
Читайте нас в Telegram, VK и Дзен
Перевод статьи Pascal Zwikirsch: Rust: Result Type Explained