In this blog post, we’ll explore strategies for streamlining error handling in Rust using two popular libraries: thiserror and anyhow. We’ll discuss their features, use cases, and provide insights on when to choose each library.
TL;DR
thiserror
simplifies the implementation of custom error type, removing boilerplatesanyhow
consolidates errors that implementstd::error::Error
- While
thiserror
provides detailed error information for specific reactions,anyhow
hides internal details
Return Different Error Types from Function
Let’s start by creating a function decode()
for illustration. The function has 3 steps:
- Read contents from a file named input
- Decode each line as a base64 string
- Print each decoded string
The challenge is determining the return type for decode since std::fs::read_to_string()
, base64 decode()
, and String::from_utf8()
each return different error types.
|
|
One approach is to use trait object: Box<dyn std::error::Error>
. This works because all those types implement std::error::Error
.
|
|
While this is suitable in some cases, it limits the caller’s ability to discern the actual error that occurred in decode()
. Then, using enum
is a good approach if it is desired to handle each error in different ways.
|
|
By implementing std::error::Error
trait, we can semantically mark AppError
as an error type.
|
|
However, this code doesn’t compile because AppError
doesn’t satisfy the constraints required by std::error::Error
, implementation of Display
and Debug
:
|
|
The definition of std::error::Error
represents the consensus of minimum requirements of an error type in Rust. An error should have two forms of description for users (Display
) and programmers (Debug
), and should provide its root cause.
|
|
The code will be like this after implementing the required traits:
|
|
Finally, we can use AppError
in decode()
:
|
|
map_err()
is used to convert std::io::Error
to AppError::ReadError
. To use ?
operator for better flow, we can implement From
trait for AppError
:
|
|
We did several things to use our custom error type fluently:
- implement
std::error::Error
- implement
Debug
andDisplay
- implement
From
These can be verbose and tedious, but fortunately, thiserror
automatically generates most of them.
Remove Boilerplates with thiserror
The code above is simplified using thiserror
:
|
|
#[error]
macro generates Display
, #[from]
macro handles From
implementations and source()
for std::error::Error
. The implementation of Debug
remains to provide detailed error messages, but #derive[Debug]
can also be used if it’s enough:
|
|
Deal with Any Error with anyhow
anyhow
offers an alternative method for simplifying error handling, which is similar to Box<dyn std::error::Error>>
approach:
|
|
It compiles since types implementing std::error::Error
can be converted to anyhow::Error
. The error message will be like:
|
|
For enhanced error messages, context()
can be used:
|
|
Then, the error message will be:
|
|
Now our error handling is streamlined thanks to the anyhow
’s type conversion and context()
.
Comparison between thiserror
and anyhow
While thiserror
and anyhow
might seem similar, they serve different purposes. thiserror
is suitable when users need to react differently based on the actual error type. On the other hand, anyhow
is effective when internal details can be hidden from the user.
In this sense, it’s often said that thiserror
is for a library, and anyhow
is for an application. This saying is true to some extent, considering that library developers tend to want to give precise information to users (programmers), and applications don’t have to show detailed error information to their users.
Conclusion
In conclusion, we’ve explored the distinctive features of thiserror and anyhow and discussed scenarios where each library shines. By choosing the right tool for the job, Rust developers can significantly simplify error handling and enhance code maintainability.
thiserror
simplifies the implementation of custom error typesanyhow
integrates anystd::error::Error
thiserror
is ideal for library development where detailed information is beneficial for users (programmers).anyhow
is Suited for applications where internal details are not crucial, providing simplified information to users.