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.

Anyhow

anyhow-badge (crates.io)

Use Result<T, anyhow::Error> or equivalently anyhow::Result<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

    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-badge

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>> {
    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-badge (lib.rs) prints fancy diagnostics upon error.

// 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 URK 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,
        // 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 just 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(())
}
mod mylib;

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(())
}

See also

eyre-badge

error-chain-badge

Do not use Error Chain⮳, which is deprecated.