Search code examples
error-handlingrusttry-catch

What Is the Rust Equivalent to a Try-Catch Statement?


Is it possible to handle multiple different errors at once instead of individually in Rust without using additional functions? In short: what is the Rust equivalent to a Try-Catch statement?

A similar feature was suggested back in 2016, but I don't know what came of it.

For example, doing something like this:

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

Instead of:

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

My program has a function which checks a variety of different places in the registry for different data values and returns some aggregate data. It would need to use many of these try-cache statements with try-catch inside of other try-catch inside of loops.


Solution

  • Results in Rust can be chained using and_then. So you can do this:

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

    or if you want a more compact syntax, you can do it with a macro:

    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