What is the Rust equivalent to a try-catch statement?

22,498

Solution 1

There is no try catch statement in Rust. The closest approach is the ? operator.

However, you do not have to create a function and a match statement to resolve it in the end. You can define a closure in your scope and use ? operator inside the closure. Then throws are held in the closure return value and you can catch this wherever you want like following:

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

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

There is a anyhow crate for the error management in Rust mostly recommended nowadays.

As an alternative, There is a failure crate for the error management in Rust. Using Failure, you can chain, convert, concatenate the errors. After converting the error types to one common type, you can catch (handle) it easily.

Solution 2

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

Solution 3

There's also an unstable feature called try_blocks (https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html, https://github.com/rust-lang/rust/issues/31436)

Usage example:

#![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,
}
Share:
22,498
Marc Guiselin
Author by

Marc Guiselin

Updated on October 06, 2021

Comments

  • Marc Guiselin
    Marc Guiselin over 2 years

    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 feature like this (First-class error handling with ? and catch) was suggested back in 2016, but I can't tell what came out of it and how a 2019 solution for such a problem might look like.

    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.

  • Shepmaster
    Shepmaster about 5 years
    Just to chime in, but failure is not the only crate that helps with error management. There are many, each with different focus.
  • Shepmaster
    Shepmaster about 5 years
    Note that your closure expression is exactly what the try block is intended to be used as.
  • L. F.
    L. F. almost 3 years
    The try keyword is reserved for future use, not deprecated, and has little connection to the (deprecated) try! macro, which has been superseded by the ? operator.
  • rsalmei
    rsalmei over 2 years
    The most recommended nowadays is anyhow.
  • Akiner Alkan
    Akiner Alkan over 2 years
    @rsalmei, thanks for pointing out the newest updated crate for that, I have edited my answer as well ;)
  • Chad
    Chad about 2 years
    For my purposes, this tweak proved to be more useful: play.rust-lang.org/…
  • Randall Coding
    Randall Coding about 2 years
    Why don't the Rust devs just have every error class inherit from some Error trait by default, so Results could always be Result< something, Error>? That seems to be the way errors behave in every other OO language.
  • Akiner Alkan
    Akiner Alkan about 2 years
    The Error trait is already applied for all error types. However, you would like to be more precise about error management with different error types to handle different errors with different behaviors. This is also very similar in the other OO languages. Extra note, OOP is not the strongest side of the Rust language imho. It has other perks than OO such as concurrent programming ensuring thread safety etc.