Что в Rust эквивалентно оператору try-catch?

Можно ли обрабатывать несколько разных ошибок одновременно, а не по отдельности в Rust без использования дополнительных функций? Вкратце: что в Rust эквивалентно оператору try-catch?

Подобная фича (Первоклассная обработка ошибок с помощью ? и catch) была предложена еще в 2016 году, но я не могу сказать, что из этого получилось и как может выглядеть решение такой проблемы в 2019 году.

Например, делая что-то вроде этого:

try {
    do_step_1()?;
    do_step_2()?;
    do_step_3()?;
    // etc
} catch {
    alert_user("Failed to perform necessary steps");
}

Вместо:

match do_steps() {
    Ok(_) => (),
    _ => alert_user("Failed to perform necessary steps")
}

// Additional function:
fn do_steps() -> Result<(), Error>{
    do_step_1()?;
    do_step_2()?;
    do_step_3()?;
    // etc
    Ok(())
}

В моей программе есть функция, которая проверяет множество различных мест в реестре на наличие различных значений данных и возвращает некоторые агрегированные данные. Потребуется использовать многие из этих операторов try-cache с try-catch внутри других операторов try-catch внутри циклов.

Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
38
0
22 788
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

В Rust нет оператора try catch. Ближайший подход — это оператор ?.

Однако вам не нужно создавать функцию и оператор match, чтобы разрешить ее в конце. Вы можете определить замыкание в своей области видимости и использовать оператор ? внутри замыкания. Затем броски сохраняются в возвращаемом значении закрытия, и вы можете поймать это, где хотите, например:

fn main() {
    let do_steps = || -> Result<(), MyError> {
        do_step_1()?;
        do_step_2()?;
        do_step_3()?;
        Ok(())
    };

    if let Err(_err) = do_steps() {
        println!("Failed to perform necessary steps");
    }
}

Игровая площадка

Is it possible to handle multiple different errors at once instead of individually in Rust without using additional functions?

Существует крейт во всяком случае для управления ошибками в Rust, наиболее рекомендуемый в настоящее время.

В качестве альтернативы существует крейт отказ для управления ошибками в Rust. Используя Failure, вы можете связать, преобразовать, объединить ошибки. После преобразования типов ошибок в один общий тип вы можете легко их поймать (обработать).

Просто чтобы вмешаться, но failure — не единственный ящик, который помогает с управлением ошибками. Их много, каждый с разной направленностью.

Shepmaster 19.04.2019 16:48

Обратите внимание, что ваше выражение замыкания точно равно для чего предназначен блок try.

Shepmaster 19.04.2019 16:51

В настоящее время наиболее рекомендуемым является anyhow.

rsalmei 05.10.2021 23:25

@rsalmei, спасибо, что указали на новейший обновленный ящик для этого, я также отредактировал свой ответ;)

Akiner Alkan 06.10.2021 09:09

Results в Rust можно связать с помощью and_then. Итак, вы можете сделать это:

if let Err(e) = do_step_1().and_then(do_step_2).and_then(do_step_3) {
    println!("Failed to perform necessary steps");
}

или, если вам нужен более компактный синтаксис, вы можете сделать это с помощью макроса:

macro_rules! attempt { // `try` is a reserved keyword
   (@recurse ($a:expr) { } catch ($e:ident) $b:block) => {
      if let Err ($e) = $a $b
   };
   (@recurse ($a:expr) { $e:expr; $($tail:tt)* } $($handler:tt)*) => {
      attempt!{@recurse ($a.and_then (|_| $e)) { $($tail)* } $($handler)*}
   };
   ({ $e:expr; $($tail:tt)* } $($handler:tt)*) => {
      attempt!{@recurse ($e) { $($tail)* } $($handler)* }
   };
}

attempt!{{
   do_step1();
   do_step2();
   do_step3();
} catch (e) {
   println!("Failed to perform necessary steps: {}", e);
}}

игровая площадка

Также есть нестабильная функция под названием try_blocks (https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html, https://github.com/rust-lang/rust/issues/31436).

Пример использования:

#![feature(try_blocks)]

fn main() {
    // you need to define the result type explicitly
    let result: Result<(), Error> = try {
        do_step_1()?;
        do_step_2()?;
        do_step_3()?;
    };

    if let Err(e) = result {
        println!("Failed to perform necessary steps, ({:?})", e);
    }
}

fn do_step_1() -> Result<(), Error> { Ok(()) }
fn do_step_2() -> Result<(), Error> { Ok(()) }
fn do_step_3() -> Result<(), Error> { Err(Error::SomeError) }

#[derive(Debug)]
enum Error {
    SomeError,
}

Понятия try и except используются крайне расплывчато. Поскольку Rust является строго типизированным языком, пользователь должен написать свои собственные методы обработки ошибок, полагаясь на предоставленные перечисления Option<T> и Result<T, E> или определяя свои собственные привычные перечисления.

См. здесь для более подробного ознакомления с обработкой ошибок с помощью перечислений.

Макрос try устарел и был заменен оператором ?, который упрощает организацию и очистку обработки ошибок, потому что он может запутаться. Основное использование оператора ? заключается в том, что он позволяет вам реализовать трейт От для варианта Result<T, E>'s Err(E).

Вот простой пример:

use std::num::ParseIntError;

//  Custom error-based enum with a single example
#[derive(Debug)]
enum Error {
    ParseIntError(ParseIntError),
    //  Other errors...
}

//  Then implement the `From` trait for each error so that the `?` operator knows what to do for each specified error.

impl From<ParseIntError> for Error {
    fn from(error: ParseIntError) -> Self {
        Self::ParseIntError(error)
    }
}

//  When using the `?` try operator, if the `Result` is an `Err` then it will basically act as `return Err(E)` returning that error value out to the current scope.  If it is `Ok(T)`, it will simply unwrap the variant.

fn main() -> Result<(), Error> {
    //  This will return the value `69` as a `u8` type
    let parsed_value_1 = "69".parse::<u8>()?;
    println!("{}", parsed_value_1);

    //  Since parsing fails here, a `ParseIntError` will be returned to the current function.  *Since the scope is the `main` function, it will automatically print the error after panicking.
    let parsed_value_2 = "poop".parse::<u8>()?;

    //  Unreachable code
    println!("{}", parsed_value_2);
    Ok(())
}

Ключевое слово try зарезервировано для использования в будущем, не устарело и мало связано с (устаревшим) макросом try!, который был заменен оператором ?.

L. F. 06.08.2021 13:47

Другие вопросы по теме