【问题标题】:What is the Rust equivalent to a try-catch statement?什么是 Rust 等价于 try-catch 语句?
【发布时间】:2019-09-09 08:28:43
【问题描述】:

是否可以一次处理多个不同的错误,而不是在 Rust 中单独处理而不使用额外的函数?简而言之:Rust 中的 try-catch 语句是什么?

类似的功能 (First-class error handling with ? and 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-catch 中使用许多这样的 try-cache 语句和 try-catch。

【问题讨论】:

    标签: error-handling rust try-catch


    【解决方案1】:

    Rust 中没有 try catch 语句。最接近的方法是? 运算符。

    但是,最终您不必创建函数和match 语句来解决它。您可以在您的范围内定义一个闭包并在闭包内使用? 运算符。然后将 throws 保存在闭包返回值中,您可以在任何地方捕获它,如下所示:

    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 中单独处理?

    现在主要推荐使用 anyhow crate 用于 Rust 中的错误管理。

    作为替代方案,Rust 中有一个 failure crate 用于错误管理。使用Failure,您可以链接、转换、连接错误。将错误类型转换为一种常见类型后,您可以轻松捕获(处理)它。

    【讨论】:

    • 只是插话,但failure 并不是唯一有助于错误管理的 crate。有很多,每个都有不同的重点。
    • 请注意,您的闭包表达式正是what the try block is intended to be used as
    • 现在最推荐的是anyhow
    • @rsalmei,感谢您指出最新更新的箱子,我也编辑了我的答案;)
    【解决方案2】:

    Rust 中的Results 可以使用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);
    }}
    

    playground

    【讨论】:

      【解决方案3】:

      还有一个不稳定的功能叫做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,
      }
      

      【讨论】:

        【解决方案4】:

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

        请参阅here,以更深入地了解使用枚举进行错误处理。

        try 宏已弃用,并已替换为 ? 运算符,这使得组织和清理错误处理变得更容易,因为它可能会变得混乱。 ? 运算符的主要用途是它允许您为 Result&lt;T, E&gt;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(())
        }
        

        【讨论】:

        • try 关键字保留供将来使用,未弃用,并且与(已弃用)try! 宏(已被? 运算符取代)几乎没有联系。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-02-16
        • 2020-12-19
        • 2019-09-25
        • 2012-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多