loudsilence
Rustaceans
Published in
4 min readFeb 22, 2024

--

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

--

--