Support me by buying an item from my wishlist, visiting my reviews site or buy me a coffee!
Getting Started with the thiserror Crate for Rust
Rust is a powerful and expressive language that offers many features for writing reliable and efficient code. One of these features is its error handling system, which allows us to handle different kinds of errors in a consistent and elegant way. However, writing custom error types and implementing the standard library’s std::error::Error
trait can be tedious and verbose. Fortunately, there is a crate that can help us with this task: thiserror
.
thiserror
is a lightweight crate that provides a convenient derive macro for the Error
trait. It allows us to define our own error types using enums, structs, or tuples, and automatically generates the necessary code for displaying and converting errors. It also supports adding contextual information and backtraces to our errors, making them easier to debug.
In this article, we will see how to use thiserror
to create and handle custom errors in Rust. We will start by installing the crate and adding it to our Cargo.toml
file:
[dependencies]
thiserror = "1.0"
Next, we will import the crate and the Error
trait in our code:
use thiserror::Error;
use std::error::Error;
Now, we are ready to define our own error type. For this example, we will use an enum to represent different kinds of errors that can occur in a hypothetical web service. We will use the #[error("...")]
attribute to provide a message for each variant, and the #[from]
attribute to indicate which variants can be converted from other error types using the ?
operator. We will also add a Backtrace
field to one of the variants to capture the stack trace of the error.
#[derive(Error, Debug)]
pub enum WebServiceError {
#[error("failed to read the key file")]
FileReadError(#[from] std::io::Error),
#[error("failed to make the API request")]
RequestError(#[from] reqwest::Error),
#[error("failed to delete the key file")]
FileDeleteError(#[from] std::io::Error),
#[error("unknown web service error")]
Unknown {
backtrace: Backtrace,
},
}
Notice how we don’t have to write any impl
blocks or Display
and From
implementations for our error type. thiserror
takes care of that for us, using the information we provided in the attributes. We can also use shorthands for interpolating fields from the error, such as {0}
for the first field, {var}
for a named field, and {:?}
for formatting with Debug
.
With our error type defined, we can use it in our functions that can potentially fail. For example, we can write a function that performs the same logic as in the example from the search_web
results:
fn make_request() -> Result<(), WebServiceError> {
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(())
}
Here, we use the ?
operator to propagate any errors that may occur while reading the file, making the request, or deleting the file. The ?
operator will automatically convert the errors into our WebServiceError
type, using the From
implementations generated by thiserror
. We also use the Result<(), WebServiceError>
type to indicate that our function can return either nothing or an error of our type.
Finally, we can handle our custom errors in the caller of our function, using pattern matching or other methods. For example, we can write a function that calls make_request
and prints the error message and the backtrace if there is an error:
fn main() {
match make_request() {
Ok(()) => println!("Request successful"),
Err(e) => {
println!("Request failed: {}", e);
if let Some(backtrace) = e.backtrace() {
println!("Backtrace: {:?}", backtrace);
}
}
}
}
Here, we use the backtrace()
method from the Error
trait to get the optional backtrace of the error, if any. We can also use the source()
method to get the underlying error that caused our error, if any.
As we can see, thiserror
makes it easy and convenient to create and handle custom errors in Rust. It reduces the boilerplate code and improves the readability and maintainability of our code. It also integrates well with the standard library’s error handling system and other crates that use the Error
trait. If you are looking for a simple and ergonomic way to work with errors in Rust, you should definitely give thiserror
a try.🦀
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