Converting Error Types
use std::error::Error; use std::fmt::{self, Display, Formatter}; use std::fs::{self, File}; use std::io::{self, Read}; #[derive(Debug)] enum ReadUsernameError { IoError(io::Error), EmptyUsername(String), } impl Error for ReadUsernameError {} impl Display for ReadUsernameError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Self::IoError(e) => write!(f, "IO error: {e}"), Self::EmptyUsername(filename) => write!(f, "Found no username in {filename}"), } } } impl From<io::Error> for ReadUsernameError { fn from(err: io::Error) -> ReadUsernameError { ReadUsernameError::IoError(err) } } fn read_username(path: &str) -> Result<String, ReadUsernameError> { let mut username = String::with_capacity(100); 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(); let username = read_username("config.dat"); println!("username or error: {username:?}"); }
Key points:
- The
username
variable can be eitherOk(string)
orErr(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 that don’t need to be no_std
to implement std::error::Error
, which requires Debug
and Display
. The Error
crate for core
is only available in nightly, so not fully no_std
compatible yet.
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.