I noticed that Rust does not have exceptions. How to do error handling in Rust and what are the common pitfalls? Are there ways to control flow with raise, catch, reraise an
Rust generally solves errors in two ways:
Unrecoverable errors. Once you panic!
, that's it. Your program or thread aborts because it encounters something it can't solve and its invariants have been violated. E.g. if you find invalid sequences in what should be a UTF-8 string.
Recoverable errors. Also called failures in some documentation. Instead of panicking, you emit a Option<T> or Result<T, E>. In these cases, you have a choice between a valid value Some(T)
/Ok(T)
respectively or an invalid value None
/Error(E)
. Generally None
serves as a null
replacement, showing that the value is missing.
Now comes the hard part. Application.
Sometimes dealing with an Option
is a pain in the neck, and you are almost guaranteed to get a value and not an error.
In those cases it's perfectly fine to use unwrap
. unwrap
turns Some(e)
and Ok(e)
into e
, otherwise it panics. Unwrap is a tool to turn your recoverable errors into unrecoverable.
if x.is_some() {
y = x.unwrap(); // perfectly safe, you just checked x is Some
}
Inside the if
-block it's perfectly fine to unwrap since it should never panic because we've already checked that it is Some
with x.is_some()
.
If you're writing a library, using unwrap
is discouraged because when it panics the user cannot handle the error. Additionally, a future update may change the invariant. Imagine if the example above had if x.is_some() || always_return_true()
. The invariant would changed, and unwrap
could panic.
?
operator / try!
macroWhat's the ?
operator or the try!
macro? A short explanation is that it either returns the value inside an Ok()
or prematurely returns error.
Here is a simplified definition of what the operator or macro expand to:
macro_rules! try {
($e:expr) => (match $e {
Ok(val) => val,
Err(err) => return Err(err),
});
}
If you use it like this:
let x = File::create("my_file.txt")?;
let x = try!(File::create("my_file.txt"));
It will convert it into this:
let x = match File::create("my_file.txt") {
Ok(val) => val,
Err(err) => return Err(err),
};
The downside is that your functions now return Result
.
Option and Result have some convenience methods that allow chaining and dealing with errors in an understandable manner. Methods like and
, and_then
, or
, or_else
, ok_or
, map_err
, etc.
For example, you could have a default value in case your value is botched.
let x: Option<i32> = None;
let guaranteed_value = x.or(Some(3)); //it's Some(3)
Or if you want to turn your Option
into a Result
.
let x = Some("foo");
assert_eq!(x.ok_or("No value found"), Ok("foo"));
let x: Option<&str> = None;
assert_eq!(x.ok_or("No value found"), Err("No value found"));
This is just a brief skim of things you can do. For more explanation, check out: