Converting Error Types

  1. use std::error::Error;
  2. use std::fmt::{self, Display, Formatter};
  3. use std::fs::{self, File};
  4. use std::io::{self, Read};
  5. #[derive(Debug)]
  6. enum ReadUsernameError {
  7. IoError(io::Error),
  8. EmptyUsername(String),
  9. }
  10. impl Error for ReadUsernameError {}
  11. impl Display for ReadUsernameError {
  12. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  13. match self {
  14. Self::IoError(e) => write!(f, "IO error: {}", e),
  15. Self::EmptyUsername(filename) => write!(f, "Found no username in {}", filename),
  16. }
  17. }
  18. }
  19. impl From<io::Error> for ReadUsernameError {
  20. fn from(err: io::Error) -> ReadUsernameError {
  21. ReadUsernameError::IoError(err)
  22. }
  23. }
  24. fn read_username(path: &str) -> Result<String, ReadUsernameError> {
  25. let mut username = String::with_capacity(100);
  26. File::open(path)?.read_to_string(&mut username)?;
  27. if username.is_empty() {
  28. return Err(ReadUsernameError::EmptyUsername(String::from(path)));
  29. }
  30. Ok(username)
  31. }
  32. fn main() {
  33. //fs::write("config.dat", "").unwrap();
  34. let username = read_username("config.dat");
  35. println!("username or error: {username:?}");
  36. }

Key points:

  • The username variable can be either Ok(string) or Err(error).
  • Use the fs::write call to test out the different scenarios: no file, empty file, file with username.

It is good practice for all error types to implement std::error::Error, which requires Debug and Display. It’s generally helpful for them to implement Clone and Eq too where possible, to make life easier for tests and consumers of your library. In this case we can’t easily do so, because io::Error doesn’t implement them.