What is the Rust equivalent to a try-catch statement?
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:
Result
s 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,
}