Перейти к основному содержимому
  1. Rust/

Операторы let-else и пропагации ошибок в Rust

821 слово·4 минуты· loading · loading · · ·Rust-middle
about-rust - Эта статья часть цикла.
Статей прочитано 0/36
0%
📚 Введение и дополнительные материалы
🟢 Начальный уровень (Rust-basic)
Не прочитана
Не прочитана
Не прочитана
Не прочитана
Не прочитана
🔵 Средний уровень (Rust-middle)

Введение
#

Написание выразительного кода требует лаконичных инструментов управления потоком выполнения. В Rust для борьбы с глубокой вложенностью кода (так называемым «callback hell» или «лестницей скобок») используются мощные операторы:

  • Оператор ? — для быстрой передачи ошибок и отсутствия значений наверх по стеку вызовов.
  • Конструкция let-else — для безопасного развертывания опровержимых шаблонов с гарантированным расходящимся выходом в случае неудачи.

Давайте изучим работу этих операторов на интерактивных примерах кода:

Операторы let-else и ?
Шаг 1/2
 1fn find_number(option: Option<i32>) -> Option<i32> {
 2    // Оператор ? возвращает значение внутри Some(x), либо делает досрочный возврат None
 3    let num = option?;
 4    Some(num * 2)
 5}
 6
 7fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> {
 8    // Оператор ? возвращает i32, либо немедленно пробрасывает ошибку Err вверх
 9    let val = s.parse::<i32>()?;
10    Ok(val * 2)
11}
12
13fn main() {
14    let result_opt = find_number(Some(21));
15    let none_opt = find_number(None);
16
17    let result_res = parse_and_double("10");
18    let err_res = parse_and_double("abc");
19
20    println!("opt success: {:?}, opt none: {:?}", result_opt, none_opt);
21    println!("res success: {:?}, res err: {:?}", result_res, err_res);
22}

Оператор ? для пропагации ошибок

  • Оператор ? используется для быстрого извлечения значений из Option и Result.
  • Для Option: если результат Some(x), извлекает x; если None, делает досрочный возврат None из функции.
  • Для Result: если результат Ok(x), извлекает x; если Err(e), делает досрочный возврат Err(e.into()).

Оператор ? (Error & Option Propagation)
#

Оператор ? используется для быстрой пропагации (передачи наверх) ошибок или отсутствующих значений. Он разворачивает успешный результат, а в случае неудачи — осуществляет немедленный досрочный возврат из текущей функции.

1. Работа с типом Option
#

Если результатом выражения является Some(r), оператор ? извлекает и возвращает r. Если результат None, происходит досрочный выход из функции с возвратом None.

// Эквивалентные конструкции:
fn foo() -> Option<u32> {
    let value = bar()?; // Использование оператора ?
    Some(value)
}

// Что происходит под капотом:
let value = match bar() {
    Some(r) => r,
    None => return None,
};

2. Работа с типом Result
#

Если результатом является Ok(r), возвращается r. Если Err(err), происходит досрочный возврат из функции значения Err(err.into()) (с автоматическим приведением типов ошибок через типаж From/Into).

// Эквивалентные конструкции:
fn foo() -> Result<u32, Error> {
    let value = bar()?; // Использование оператора ?
    Ok(value)
}

// Что происходит под капотом:
let value = match bar() {
    Ok(r) => r,
    Err(err) => return Err(err.into()),
};

Конструкция let - else (Diverging let)
#

Синтаксис let-else позволяет связать переменную по шаблону в случае успеха, либо выполнить расходящийся (diverging) блок кода в случае неудачи.

  • Синтаксис: let pattern = expression else { ... };. Обратите внимание на обязательное наличие точки с запятой ; после закрывающей фигурной скобки.
  • Критическое правило блока else: Блок else обязан быть расходящимся (diverging), то есть он не имеет права возвращать управление в текущую область видимости обычным путем. Он обязан завершаться операторами:
    • return (выход из функции)
    • break (выход из цикла)
    • continue (переход на следующую итерацию цикла)
    • panic!() (завершение потока с ошибкой)

Различие шаблонов
#

Обычный let работает только с неопровержимыми (irrefutable) шаблонами, которые всегда совпадают (например, кортежи, ссылки или простые переменные). Для опровержимых (refutable) шаблонов, таких как Some(x), необходим let-else или if let.

// Примеры неопровержимых шаблонов для классического let:
let a = 1 + 2;
let (a, b) = some_tuple;

// Использование let-else для опровержимых шаблонов:
let Some(x) = some_option else { return; };
let Some(x) = some_option else { break; };
let Some(x) = some_option else { continue; };
let Some(x) = some_option else { panic!(); };

Практический пример обработки потока событий в цикле
#

Использование let-else значительно уменьшает уровень вложенности кода по сравнению с match или if let:

loop {
    // Если get_event() вернул Err, прерываем цикл
    let Ok(event) = get_event() else {
        break;
    };
    
    // Если event равен None, переходим к следующей итерации
    let Some(event) = event else {
        continue;
    };
    
    // Переменная event гарантированно развернута и валидна в этой точке
    handle_event(event);
}

Проверь свои знания!
#

Пройдите короткий интерактивный тест, чтобы закрепить понимание let-else и ?.

История обновлений

  • Добавил интерактивные слайды и тесты по let-else и оператору пропагации ошибок ?.
Статья прочитана
Пожалуйста, оцените насколько статья была вам полезна и понятна
Цикл статей
about-rust - Эта статья часть цикла.
Статей прочитано 0/36
0%
📚 Введение и дополнительные материалы
🟢 Начальный уровень (Rust-basic)
Не прочитана
Не прочитана
Не прочитана
Не прочитана
Не прочитана
🔵 Средний уровень (Rust-middle)

Связанные статьи

Hash Map в Rust

·962 слова·5 минут· loading · loading ·Rust-basic