Deriving Error Enums

The thiserror crate is a popular way to create an error enum like we did on the previous page:

  1. use std::{fs, io};
  2. use std::io::Read;
  3. use thiserror::Error;
  4. #[derive(Debug, Error)]
  5. enum ReadUsernameError {
  6. #[error("Could not read: {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. }

thiserror’s derive macro automatically implements std::error::Error, and optionally Display (if the #[error(...)] attributes are provided) and From (if the #[from] attribute is added). It also works for structs.

It doesn’t affect your public API, which makes it good for libraries.