# Errors using thiserror For errors we use the crate `thiserror` because it is well-maintained, pragmatic, and provides detailed errors suitable for libraries. We removed the crate `error-chain` because it wasn't well-maintained. ## pub enum Error The pattern for error definition looks like this: ```rust #[derive(thiserror::Error, Debug)] pub enum Error { #[error("AlfaBravo ➡ {0:?}")] AlfaBravo(std::io::Error), #[error("CharlieDelta ➡ echo: {echo:?}, foxtrot: {foxtrot:?}")] CharlieDelta { echo: String, foxtrot: String, } } ``` ### Our convention Our convention for `pub enum Error`: * The enum is named `Error`, not anything else. A side effect of this convention: in each file, the file's enum `Error` convention supersedes any other `Error` such as `std::error::Error`, i.e. in each file, any other `Error` must be fully qualified. * The error message starts with the enum variant name and a Unicode right arrow. * The error message for a struct includes each field and its debug representation, so long as it's practical, i.e. we want to behave akin to a library error, and we want print as much information as possible for developers who are debugging. * For wrapping errors, prefer using the simple form. ## Catch errors Our preferred pattern for handling a `Result` uses `map_or_else` to return the error `err` or unwrap the value `val` like this: ```rust foo().map_or_else( |err| MyError::StdIoError(err), |val| val.ok() )?; ``` ### Catch errors in main Our convention is a file `main.rs` where the setup happens: ```rust fn main() { env_logger::init(); match crate::app::run::run() { Ok(()) => { std::process::exit(0); } Err(err) => { error!("{:?}", err); std::process::exit(1); } } } ``` Our convention is a file `run.rs` where the work happens: ```rust pub(crate) fn run() -> Result<()> { … Ok(()) } ``` ## Wrap errors Say you have a file `alfa.rs` with any typical function that can return an error such as: ```rust pub fn positive(x: i8) -> Result<(), Error> { if x > 0 { return x } else { Error::Parma(x) } } #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Parma ➡ {0:?}")] Param(i8), } ``` Say you also have a file `bravo.rs` that calls `alfa.rs` such as: ```rust pub fn f(x: i8) -> Result<(), Error> { crate::alfa::positive(3) .map_or_else( |err| Error::Wrap(err), |val| val.ok() )? } #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Wrap ➡ {0:?}")] Wrap(crate::alfa::Error) } ```