Rust语句与try-catch语句的等价形式是什么?

brqmpdu1  于 2023-02-04  发布在  其他
关注(0)|答案(6)|浏览(227)

在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-cache语句,其中try-catch在循环中,其他try-catch在循环中。

1l5u6lss

1l5u6lss1#

Rust中没有try catch语句,最接近的方法是?操作符。
但是,您不必创建函数和match语句来最终解析它。您可以在作用域中定义闭包,并在闭包中使用?运算符。然后,throw保存在闭包返回值中,您可以在任何需要的位置捕获它,如下所示:

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中有一个anyhow板条箱用于错误管理,这是现在最受推荐的。
作为一种替代方案,Rust中有一个failure机箱用于错误管理。使用Failure,您可以链接、转换、连接错误。在将错误类型转换为一种常见类型后,您可以轻松捕获(处理)它。

e1xvtsh3

e1xvtsh32#

Rust中的Result可以使用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

agyaoht7

agyaoht73#

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

hgtggwj04#

我认为match表达式等效于try/catch

match get_weather(location) {
   Ok(report) => {
                  display_weather(location, &report);
                 }
   Err(err) => {
                 println!("error querying the weather: {}", err);
                 // or write a better logic
   }
}

我们尝试从API获取天气报告,如果我们的请求失败,处理错误,否则显示结果。

ghg1uchk

ghg1uchk5#

不确定这是否被认为是惯用的Rust,但是可以使用匿名闭包来实现类似于try/catch的语法:

fn do_step_1() -> Result<(), String> { Ok(()) }
fn do_step_2() -> Result<(), String> { Err("error at step 2".to_string()) }
fn do_step_3() -> Result<(), String> { Ok(()) }
fn alert_user(s: &str) { println!("{}", s); }

fn main() {
    (|| {
        do_step_1()?;
        do_step_2()?;
        do_step_3()?;
        Ok(())
    })().unwrap_or_else(|_err: String| {
        alert_user("Failed to perform the necessary steps");
    })
}
5ssjco0h

5ssjco0h6#

tryexcept概念的使用非常模糊,因为Rust是强类型语言,用户必须依赖提供的Option<T>Result<T, E>枚举或定义自己习惯的枚举来编写自己的错误处理方法。
有关使用枚举进行错误处理的更深入的内容,请参见此处。
try宏已被弃用,并已被?操作符所取代,这使得组织和清理错误处理变得更容易,因为它可能会变得混乱。?操作符的主要用途是,它允许您为Result<T, E>Err(E)变体实现From trait。
下面是一个基本示例:

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

相关问题