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");
}
}
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);
}}
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,
}
Marc Guiselin
Updated on October 06, 2021Comments
-
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
?
andcatch
) 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 about 5 yearsJust to chime in, but
failure
is not the only crate that helps with error management. There are many, each with different focus. -
Shepmaster about 5 yearsNote that your closure expression is exactly what the
try
block is intended to be used as. -
L. F. almost 3 yearsThe
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 over 2 yearsThe most recommended nowadays is
anyhow
. -
Akiner Alkan over 2 years@rsalmei, thanks for pointing out the newest updated crate for that, I have edited my answer as well ;)
-
Chad about 2 yearsFor my purposes, this tweak proved to be more useful: play.rust-lang.org/…
-
Randall Coding about 2 yearsWhy 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 about 2 yearsThe 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.