Result и оператор ?

Result делает ошибки видимыми в типе; оператор ? позволяет элегантно их пробрасывать.

Result<T, E> — тип результата операции, которая может не удаться: Ok(значение) при успехе или Err(ошибка) при сбое.

Ошибки как значения

В Rust нет исключений. Операция, которая может провалиться, возвращает Result<T, E> — ещё одно перечисление стандартной библиотеки. Ошибка становится обычным значением, которое нельзя случайно проигнорировать.

// так Result определён в стандартной библиотеке
enum Result<T, E> {
    Ok(T),   // успех, внутри результат
    Err(E),  // ошибка, внутри её причина
}

Поскольку результат «завёрнут», вы обязаны разобрать его, прежде чем добраться до значения, — а значит, обязаны учесть возможность сбоя.

fn parse_age(s: &str) -> Result<u32, std::num::ParseIntError> {
    s.parse::<u32>() // parse возвращает Result
}

fn main() {
    match parse_age("30") {
        Ok(age) => println!("возраст: {age}"),
        Err(e) => println!("ошибка: {e}"),
    }
    match parse_age("abc") {
        Ok(age) => println!("возраст: {age}"),
        Err(e) => println!("ошибка: {e}"),
    }
}

Вывод:

возраст: 30
ошибка: invalid digit found in string

Оператор ? — пробрасывание ошибок

Писать match на каждый Result утомительно, особенно когда операций несколько подряд. Для этого есть оператор ?: он разворачивает Ok и продолжает, а при Err немедленно возвращает эту ошибку из функции. По сути это «дай значение или верни ошибку наверх».

fn sum_two(a: &str, b: &str) -> Result<i32, std::num::ParseIntError> {
    let x = a.parse::<i32>()?; // если Err — выходим из функции с этой ошибкой
    let y = b.parse::<i32>()?;
    Ok(x + y)
}

fn main() {
    println!("{:?}", sum_two("10", "20"));  // Ok(30)
    println!("{:?}", sum_two("10", "xx"));  // Err(...)
}

Вывод:

Ok(30)
Err(ParseIntError { kind: InvalidDigit })

Без ? тот же код потребовал бы двух вложенных match. Оператор ? работает в функциях, которые сами возвращают Result (или Option), и делает «счастливый путь» линейным и читаемым.

Полезные методы Result

МетодЧто делает
.is_ok() / .is_err()проверить исход без распаковки
.unwrap()достать Ok или паника при Err
.unwrap_or(d)значение из Ok или запасное d
.ok()превратить в Option (ошибку отбросить)

unwrap() удобен для быстрых прототипов и примеров, но в надёжном коде его избегают: он превращает ошибку в панику. Лучше обработать Err явно или пробросить через ?.

Итог

  • Ошибки в Rust — это значения: Result<T, E> с вариантами Ok и Err.
  • Оператор ? разворачивает Ok или возвращает Err наверх, делая код линейным.
  • ? работает в функциях, возвращающих Result или Option.
Проверьте себя
1. Как в Rust функция сообщает, что операция может не удаться?
AБросает исключение
BВозвращает Result<T, E> с вариантами Ok и Err
CВозвращает null
DПечатает ошибку
2. Что делает оператор ? при значении Err?
AИгнорирует ошибку
BНемедленно возвращает эту ошибку из текущей функции
CПаникует всегда
DПревращает ошибку в Ok
3. В каких функциях можно использовать оператор ?
AВ любых
BВ функциях, которые сами возвращают Result или Option
CТолько в main
DТолько в тестах
Поддержать проект