thiserror

The thiserror crate provides macros to help avoid boilerplate when defining error types. It provides derive macros that assist in implementing From<T>, Display, and the Error trait.

  1. use std::io::Read;
  2. use std::{fs, io};
  3. use thiserror::Error;
  4. #[derive(Debug, Error)]
  5. enum ReadUsernameError {
  6.     #[error("I/O error: {0}")]
  7.     IoError(#[from] io::Error),
  8.     #[error("Found no username in {0}")]
  9.     EmptyUsername(String),
  10. }
  11. fn read_username(path: &str) -> Result<String, ReadUsernameError> {
  12.     let mut username = String::with_capacity(100);
  13.     fs::File::open(path)?.read_to_string(&mut username)?;
  14.     if username.is_empty() {
  15.         return Err(ReadUsernameError::EmptyUsername(String::from(path)));
  16.     }
  17.     Ok(username)
  18. }
  19. fn main() {
  20.     //fs::write("config.dat", "").unwrap();
  21.     match read_username("config.dat") {
  22.         Ok(username) => println!("Username: {username}"),
  23.         Err(err) => println!("Error: {err:?}"),
  24.     }
  25. }

This slide should take about 5 minutes.

  • The Error derive macro is provided by thiserror, and has lots of useful attributes to help define error types in a compact way.
  • The message from #[error] is used to derive the Display trait.
  • Note that the (thiserror::)Error derive macro, while it has the effect of implementing the (std::error::)Error trait, is not the same this; traits and macros do not share a namespace.