Dynamic Error Types

Sometimes we want to allow any type of error to be returned without writing our own enum covering all the different possibilities. std::error::Error makes this easy.

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

This saves on code, but gives up the ability to cleanly handle different error cases differently in the program. As such it’s generally not a good idea to use Box<dyn Error> in the public API of a library, but it can be a good option in a program where you just want to display the error message somewhere.