什么是Rust等同于try-catch语句?

时间:2019-04-19 01:34:52

标签: error-handling rust try-catch

是否可以一次处理多个不同的错误,而不是在Rust 中不使用其他功能而单独处理?简而言之:Rust等价于try-catch语句?

早在2016年就建议使用类似(First-class error handling with ? and catch)的功能,但我不知道它产生了什么,以及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一起用于循环内。

4 个答案:

答案 0 :(得分:5)

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");
    }
}

Playground

  

是否可以一次处理多个不同的错误,而不用在Rust中单独处理而无需使用其他功能?

是的,有可能。 Rust中有一个failure的错误管理箱。使用Failure,您可以链接,转换,连接错误。将错误类型转换为一种常见类型后,您可以轻松地捕获(处理)它。

答案 1 :(得分:5)

可以使用Result链接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);
}}

documentation

答案 2 :(得分:1)

还有一个不稳定的功能,称为try_blockshttps://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.htmlhttps://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,
}

答案 3 :(得分:-1)

tryexcept 概念的使用非常模糊。由于 Rust 是一种强类型语言,因此用户必须通过依赖提供的 Option<T>Result<T, E> 枚举或通过定义自己习惯的枚举来编写自己的方法来处理错误。

有关使用枚举进行错误处理的更深入阅读,请参阅 here

try 宏已弃用并已替换为 ? 运算符,这使得组织和清理错误处理变得更容易,因为它可能会变得混乱。 ? 运算符的主要用途是它允许您为 Result<T, E>Err(E) 变体实现 From 特征。

这是一个基本示例:

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(())
}