Support me by buying an item from my wishlist, visiting my reviews site or buy me a coffee!
A Comprehensive Guide to robust code with thiserror
for Rust
Error handling in Rust is a critical aspect of writing robust and reliable code. The thiserror
crate is a powerful tool that simplifies the implementation of custom error types, making your code more concise and expressive. In this article, we’ll dive into the advanced usage of thiserror
, assuming you’re already familiar with the basics of Rust error handling.
What Is the thiserror
Crate?
The thiserror
crate provides a convenient derive macro for the standard library’s std::error::Error
trait. It was authored by David Tolnay, the same legendary developer behind other popular crates like serde
, syn
, quote
, and anyhow
. While thiserror
may appear lightweight on the surface, its impact on code readability and maintainability is substantial.
The Basics: Error Handling Without thiserror
Let’s start with a simple example to illustrate the need for thiserror
. Imagine you have a web application service function that reads a key file from disk, fetches content from an API using the read key, and deletes the key file afterward. Here’s a basic implementation without using thiserror
:
fn make_request() -> Result<(), Box<dyn std::error::Error>> {
let key = std::fs::read_to_string("some-key-file")?;
reqwest::blocking::get(format!("https://httpbin.org/key/{}", key))?.error_for_status()?;
std::fs::remove_file(key)?;
Ok(())
}
In this example:
- We use
Box<dyn std::error::Error>
as our returned error type to handle all errors uniformly. - The
?
operator converts different error types into the same boxed error type. - However, if the caller wants to handle each error differently (e.g., send different HTTP status codes), things get more complex.
Introducing thiserror
: Custom Error Types Made Easy
The thiserror
crate simplifies error handling by allowing you to define custom error types with ease. Let’s refactor our code using thiserror
:
- Add
thiserror = "1"
to yourCargo.toml
dependencies. - Derive the
Error
trait for your custom error type:
use thiserror::Error;
#[derive(Error, Debug)]
enum CustomError {
#[error("data store disconnected")]
Disconnect(#[from] std::io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
3. Refactor your make_request
function:
fn make_request() -> Result<(), CustomError> {
let key = std::fs::read_to_string("some-key-file")?;
reqwest::blocking::get(format!("https://httpbin.org/key/{}", key))?.error_for_status()?;
std::fs::remove_file(key)?;
Ok(())
}
Now, with CustomError
, you can handle different error scenarios more intuitively. For example, you can match specific variants of CustomError
and respond accordingly.
Additional Features of thiserror
- Custom Messages: You can provide custom error messages for each variant using
#[error("...")]
. - Interpolating Fields: The messages support field interpolation, making it easy to include relevant information in the error message.
- Source Errors: Use
#[from]
to generate aFrom
implementation for each variant containing a source error (e.g.,io::Error
). - Opaque Error Types: Hide implementation details behind an opaque error type, allowing evolution without breaking the public API.
Conclusion
The thiserror
crate empowers Rust developers to create expressive and maintainable error handling code. Whether you’re building libraries or applications, thiserror
is a valuable addition to your toolbox. So go ahead, embrace custom error types, and write more robust Rust code!
Remember, error handling is an essential part of software development, and with thiserror
, it becomes a joy rather than a chore.🦀
Support me by buying an item from my wishlist, visiting my reviews site or buy me a coffee!
Learn More
Hey Rustaceans!
Thanks for being an awesome part of the community! Before you head off, here are a few ways to stay connected and show your love:
- Give us a clap! Your appreciation helps us keep creating valuable content.
- Become a contributor! ✍️ We’d love to hear your voice. Learn how to write for us.
- Stay in the loop! Subscribe to the Rust Bytes Newsletter for the latest news and insights.
- Support our work! ☕ Buy us a coffee.
- Connect with us: X