Custom Errors
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.
Simplify Error Handling and Attach Context to Errors with anyhow
anyhow
⮳ provides anyhow::Error
, 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.
You can use anyhow
to:
- Create errors easily.
- Attach context to help the person troubleshooting the error understand where things went wrong.
- Work with dynamic error types (
Box<dyn Error>
) conveniently.
//! Demonstrates the use of the `anyhow` crate for error handling. use anyhow::Context; use anyhow::Result; /// Simulates a function that returns an error. fn do_something() -> Result<()> { Err(anyhow::Error::msg("Some Error")) } fn main() -> anyhow::Result<()> { // Provide context to the error: do_something().context("Failed to do the important thing")?; // Demonstrates adding context to a standard library error. // The closure is only evaluated if there is an 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
⮳.
Create Custom Error Types Declaratively with thisError
thiserror
⮳ provides a convenient derive
⮳ macro for the standard library's std::error::Error
⮳ trait.
// Import the `Error` derive macro from the `thiserror` crate. use thiserror::Error; /// Define a custom error type `DataStoreError` using the `thiserror` crate. /// The `Error` derive macro automatically implements the `std::error::Error` /// trait. #[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>> { // For demonstration purposes, return an error of type // `DataStoreError::Unknown`. 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)
//! Demonstrates the use of the `thiserror` crate for creating custom error //! types. This example shows how to wrap an underlying `std::io::Error` and //! customize its display. use thiserror::Error; /// A custom error type that wraps an underlying `std::io::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, } /// Implement the `Display` trait for `MyError` to customize its string /// representation. impl std::fmt::Display for MyError { fn fmt( &self, f: &mut std::fmt::Formatter<'_>, ) -> Result<(), std::fmt::Error> { write!(f, "{}", self.msg) } } /// An example function that demonstrates how to create and return a `MyError`. 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); } } }
Provide Human-readable Diagnostics with miette
miette
⮳ is a fancy diagnostic reporting library and protocol.
//! This example demonstrates how to define custom error types using `miette` //! and `thiserror`. It showcases various features like error codes, URLs, help //! text, source code snippets, and wrapping other errors. /// Example code for a library. /// In real-life, this code would be in a separate crate. /// /// We define unique error types and error wrappers below. mod mylib { 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), } /// Another error: #[derive(Error, Diagnostic, Debug)] #[error("another error")] pub struct AnotherError { #[label("here")] pub at: SourceSpan, } /// Example function that fails. 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<()>`. /// /// This `main` function demonstrates how to use the custom error types defined /// in the `mylib` module. It calls the `this_fails` function, which returns a /// `Result`, and if an error occurs, `miette` will format and print it nicely. /// /// ## Note: /// You can swap out the default reporter for a custom one using /// `miette::set_hook()`. fn main() -> Result<()> { mylib::this_fails()?; Ok(()) }
Create Colorful and User-friendly Error Reports with 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.
//! Demonstrates how to use the `color-eyre` crate for enhanced error reporting. //! //! `color_eyre` provides beautiful error reporting and backtraces with colors, //! making it easier to understand and debug errors. //! It's especially useful for debugging in command-line applications. use color_eyre::eyre::Result; use color_eyre::eyre::eyre; fn main() -> Result<()> { // Initializes color_eyre's error reporting hook. This hook intercepts // panics and errors, formatting them with colors and backtraces. color_eyre::install()?; let result = divide(10, 0)?; println!("Result: {}", result); Ok(()) } fn divide(a: i32, b: i32) -> Result<i32> { if b == 0 { // Use the `eyre!` macro to construct an ad-hoc error from a string. Err(eyre!( "Attempted to divide {} by zero in the `divide` function.", a )) } else { Ok(a / b) } }
See Also
Do not use Error Chain⮳, which is deprecated.