loudsilence
Rustaceans
Published in
3 min readFeb 27, 2024

--

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:

  1. Add thiserror = "1" to your Cargo.toml dependencies.
  2. 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 a From 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

--

--