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.
use std::io::Read; use std::{fs, io}; use thiserror::Error; #[derive(Debug, Error)] enum ReadUsernameError { #[error("I/O error: {0}")] IoError(#[from] io::Error), #[error("Found no username in {0}")] EmptyUsername(String), } fn read_username(path: &str) -> Result<String, ReadUsernameError> { let mut username = String::with_capacity(100); fs::File::open(path)?.read_to_string(&mut username)?; if username.is_empty() { return Err(ReadUsernameError::EmptyUsername(String::from(path))); } Ok(username) } fn main() { //fs::write("config.dat", "").unwrap(); match read_username("config.dat") { Ok(username) => println!("Username: {username}"), Err(err) => println!("Error: {err:?}"), } }
This slide should take about 5 minutes. 
                    - The Errorderive macro is provided bythiserror, and has lots of useful attributes to help define error types in a compact way.
- The message from #[error]is used to derive theDisplaytrait.
- Note that the (thiserror::)Errorderive macro, while it has the effect of implementing the (std::error::)Errortrait, is not the same this; traits and macros do not share a namespace.