Error Handling
Panic is a kind of error that should never happen.
Result
s typically represent problems caused by things outside the program, like erroneous input, a network outage, or a permissions problem.
Panic
Rust can either unwind the stack when a panic happens or abort the process. Unwinding is the default.
Unwinding
If you set the RUST_BACKTRACE
environment variable, Rust will dump the stack on panic. Panic is per thread. One thread can be panicking while other threads are going on about their normal business.
There is also a way to catch stack unwinding, allowing the thread to survive and continue running. The standard library function std::panic::catch_unwind()
does this.
Aborting
Stack unwinding is the default panic behavior, but there are two circumstances in which Rust does not try to unwind the stack.
If a
.drop()
method triggers a second panic while Rust is still trying to clean up after the first, this is considered fatal. Rust stops unwinding and aborts the whole process.Also, Rust’s panic behavior is customizable. If you compile with
-C panic=abort
, the first panic in your program immediately aborts the process.
Result
functions that can fail have a return type that says so:
Catching Errors
The most thorough way of dealing with a Result
:
Result<T, E>
offers a variety of methods that are useful in particular common cases. Each of these methods has a match
expression in its implementation.
Methods for working with references in a Result
:
result.as_ref()
Converts a
Result<T, E>
to aResult<&T, &E>
.result.as_mut()
This is the same, but borrows a mutable reference. The return type is
Result<&mut T, &mut E>
.
Result Type Aliases
Sometimes you’ll see Rust documentation that seems to omit the error type of a Result
:
The standard library’s std::io
module includes this line of code:
This defines a public type std::io::Result<T>
. It’s an alias for Result<T, E>
, but hardcodes std::io::Error
as the error type.
Printing Errors
The standard library defines several error types with boring names: std::io::Error
, std::fmt::Error
, std::str::Utf8Error
, and so on. All of them implement a common interface, the std::error::Error
trait, which means they share the following features and methods:
println!()
: All error types are printable using this. Printing an error with the {}
format specifier typically displays only a brief error message.
err.to_string()
: Returns an error message as a String
.
err.source()
: Returns an Option
of the underlying error, if any, that caused err
.
The standard library’s error types do not include a stack trace, but the popular anyhow
crate provides a ready-made error type that does, when used with an unstable version of the Rust compiler.
Propagating Errors
If an error occurs, we usually want to let our caller deal with it. We want errors to propagate up the call stack.
On success, it unwraps the
Result
to get the success value inside. The type ofweather
here is notResult<WeatherReport, io::Error>
but simplyWeatherReport
.On error, it immediately returns from the enclosing function, passing the error result up the call chain. To ensure that this works,
?
can only be used on aResult
in functions that have aResult
return type.
?
also works similarly with the Option
type. In a function that returns Option
, you can use ?
to unwrap a value and return early in the case of None
.
[!important]
Result
andOption
is defiend in Rust Core Library and can be used in nostd environment.
Working with Multiple Error Types
All of the standard library error types can be converted to the type Box<dyn std::error::Error + Send + Sync + 'static>
. dyn std::error::Error
represents “any error,” and Send + Sync + 'static
makes it safe to pass between threads.
Incidentally, the ?
operator does the automatic conversion from random error types to GenericError
using a standard method that you can use yourself. To convert any error to the GenericError
type, call GenericError::from()
:
If you’re calling a function that returns a GenericResult
and you want to handle one particular kind of error but let all others propagate out, use the generic method error.downcast_ref::<ErrorType>()
. It borrows a reference to the error, if it happens to be the particular type of error you’re looking for:
Dealing with Errors That “Can’t Happen”
If we are sure that an error cannot happen, then we can use unwrap()
on the Result
type:
However, this could cause panics since 999999999999
will cause an overflow error even if can be parsed as an number.
Ignoring Errors
writeln!(stderr(), "error: {}", err);
can return a Result
, which could indicate an Err
. We can ignore this by:
Declaring a Custom Error Type
You can make error handling much easier by the thiserror
crate:
Last updated