Custom Errors
Recipe | Crates | Categories |
---|---|---|
anyhow | ||
thisError | ||
miette | ||
color-eyre |
Use anyhow
⮳ if you don't care what error type your functions return, you just want it to be easy. This is common in application code. Use thiserror
⮳ if you are a library that wants to design your own dedicated error type(s) so that on failures the caller gets exactly the information that you choose.
anyhow
anyhow
⮳ provides a flexible concrete Error type built on std::error::Error
⮳.
Use Result<T, anyhow::Error>
or equivalently anyhow::Result<a name="a010"></a><T>
⮳ as the return type of any fallible function.
use anyhow::Context; use anyhow::Result; fn do_something() -> Result<()> { Err(anyhow::Error::msg("Some Error")) } fn main() -> anyhow::Result<()> { // ... do_something().context("Failed to do the important thing")?; // Provide context to the error let _content = std::fs::read("/notafile.txt") .with_context(|| "Failed to read instrs from file".to_string())?; Ok(()) }
Anyhow works with any error type that has an impl of std::error::Error
⮳, including ones defined in your crate e.g. using thiserror
⮳.
thisError
thiserror
⮳ provides a convenient derive
⮳ macro for the standard library's std::error::Error
⮳ trait.
use thiserror::Error; #[derive(Error, Debug)] pub enum DataStoreError { // A Display impl is generated for your error if you provide // #[error("...")] messages on the struct or each variant of your enum #[error("data store disconnected")] Disconnect(#[from] std::io::Error), /* A From impl is generated for * each variant containing * a #[from] attribute. */ #[error("the data for key `{0}` is not available")] Redaction(String), #[error("invalid header (expected {expected:?}, found {found:?})")] InvalidHeader { expected: String, found: String }, #[error("unknown data store error")] Unknown, #[error(transparent)] // Forward the source and Display methods straight through to an // underlying error without adding an additional message. Other(#[from] anyhow::Error), } fn main() -> Result<(), Box<dyn std::error::Error>> { // Return an error: Err(DataStoreError::Unknown)?; Ok(()) }
The #[error(...)]
messages support a shorthand for interpolating fields from the error.
#[error("{var}")] //⟶ write!("{}", self.var)
#[error("{0}")] //⟶ write!("{}", self.0)
#[error("{var:?}")] //⟶ write!("{:?}", self.var)
#[error("{0:?}")] //⟶ write!("{:?}", self.0)
use thiserror::Error; #[derive(Error, Debug)] pub struct MyError { msg: String, // The Error trait's source() method is implemented to return whichever // field has a #[source] attribute or is named source, if any. This is // for identifying the underlying lower level error that caused your // error. #[from] implies #[source]. Any error type that implements // `std::error::Error` or dereferences to `dyn std::error::Error` will work // as a source. #[source] source: std::io::Error, // Automatically detected to implement provide() // backtrace: std::backtrace::Backtrace, } impl std::fmt::Display for MyError { fn fmt( &self, f: &mut std::fmt::Formatter<'_>, ) -> Result<(), std::fmt::Error> { write!(f, "{}", self.msg) } } fn example() -> Result<(), Box<dyn std::error::Error>> { let io_error = std::io::Error::new(std::io::ErrorKind::Other, "oh no!"); Err(Box::new(MyError { msg: "Error message".to_string(), source: io_error, })) } fn main() { match example() { Ok(_) => { println!("Got OK"); } Err(err) => { println!("Got {}", err); } } }
miette
miette
⮳ is a fancy diagnostic reporting library and protocol.
mod mylib { //! library code: define unique error types and error wrappers use miette::Diagnostic; use miette::NamedSource; use miette::Result; use miette::SourceSpan; // You can derive a `Diagnostic` from any `std::error::Error` type. // `thiserror` plays nicely with `miette` use thiserror::Error; #[derive(Error, Diagnostic, Debug)] pub enum MyLibError { #[error("A bad thing happened!")] // provided by `thisError` #[diagnostic( // Use `#[diagnostic(code(...))]` to set the unique code for this error. code(my_lib::bad_thing), // Set the URL that will be displayed as an actual link in supported terminals. // `url(docsrs)` automatically create a link to this diagnostic on docs.rs // or use a custom URL like `url("https://my_website.com/error_codes#{}", self.code)` url(docsrs), // Supply help text help("try doing it better next time?"))] BadThingHappened, #[error("Something went wrong!")] SomethingWentWrong { // The Source that we're gonna be printing snippets out of. // This can be a String if you don't have or care about file names. #[source_code] src: NamedSource<String>, // Snippets and highlights can be included in the diagnostic! // You may also use `(usize, usize)`, the byte-offset and length // into an associated SourceCode or // `Option<SourceSpan>` #[label("This bit highlighted here is the problem")] bad_bit: SourceSpan, // Programmatically supply the help text #[help] advice: Option<String>, // Can also be `String` // Related errors #[related] others: Vec<MyLibError>, }, // Wrap an Error #[error(transparent)] // Forward the source and Display methods // straight through to the underlying error. #[diagnostic(code(my_lib::io_error))] IoError(#[from] std::io::Error), // Wrap another Diagnostic // Use `#[diagnostic(transparent)]` to wrap another `[Diagnostic]` // You won't see labels otherwise #[error(transparent)] #[diagnostic(transparent)] AnotherError(#[from] AnotherError), } #[derive(Error, Diagnostic, Debug)] #[error("another error")] pub struct AnotherError { #[label("here")] pub at: SourceSpan, } pub fn this_fails() -> Result<()> { // You can use plain strings as a `Source`, // or anything that implements the one-method `Source` trait. let src = "source\n text\n here".to_string(); // You may also use `map_err(|error| { // error.with_source_code(String::from("source code")) })` later. Err(MyLibError::SomethingWentWrong { src: NamedSource::new("bad_file.rs", src), bad_bit: (9, 4).into(), advice: Some("Some help text".to_string()), others: vec![MyLibError::BadThingHappened], })?; Ok(()) } } use miette::Result; /// To get errors printed nicely in application code, just return a /// `Result<()>` /// /// Note: You can swap out the default reporter for a /// custom one using `miette::set_hook()` fn main() -> Result<()> { mylib::this_fails()?; Ok(()) }
color-eyre
color-eyre
⮳ is an error report handler for panics and eyre::Reports
for colorful, consistent, and well formatted error reports for all kinds of errors.
It is a fork of anyhow
⮳ anyhow
⮳ that gives you more control over the format of the generated error messages. It is recommended if you intend to present error messages to end users. Otherwise anyhow
⮳ is simpler.
use color_eyre::eyre::Result; // `color_eyre` provides beautiful error reporting and backtraces with colors. // It's especially useful for debugging. fn main() -> Result<()> { color_eyre::install()?; let result = divide(10, 0)?; println!("Result: {}", result); Ok(()) } fn divide(a: i32, b: i32) -> Result<i32> { if b == 0 { Err(color_eyre::eyre::eyre!("Division by zero")) } else { Ok(a / b) } }
See Also
Do not use Error Chain⮳, which is deprecated.