thiserror

repository·master·Indexed 24 days ago

https://github.com/dtolnay/thiserror

A library providing a derive(Error) macro for implementing the standard library's std::error::Error trait. Designed for library authors to define custom, structured error types with minimal boilerplate using attributes like #[error] for Display messages, #[from] for From implementations, and #[source] for error sources.

Tokens
2.3K
Snippets
10
Records
13
Agent score
89%

What's inside thiserror

  1. Choose between thiserror and anyhow

    master

    Decide which library to use based on your project type:

    • Use thiserror if you are writing library-like code and want to design dedicated error types so callers receive specific information.
    • Use anyhow if you are writing application-like code and want a convenient single error type without caring about the specific error type returned.
  2. Use the `Error` derive macro

    master

    The thiserror library provides a derive(Error) macro to automatically implement the std::error::Error trait for enums and structs. This allows you to define custom error types with minimal boilerplate while maintaining full compatibility with the standard library's error handling patterns.

    use thiserror::Error;
    use std::io;
    
    #[derive(Error, Debug)]
    pub enum DataStoreError {
        #[error("data store disconnected")]
        Disconnect(#[from] io::Error),
        #[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,
    }
  3. Implement the Error trait with derive(Error)

    master

    Use #[derive(Error, Debug)] on enums or structs to automatically implement std::error::Error. You must provide #[error("...")] messages to generate the Display implementation.

    use thiserror::Error;
    
    #[derive(Error, Debug)]
    pub enum DataStoreError {
        #[error("data store disconnected")]
        Disconnect(#[from] io::Error),
        #[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,
    }
  4. Define the error source with #[source]

    master

    The source() method of the Error trait is implemented by looking for a field with the #[source] attribute or a field named source. Note that #[from] implies #[source], so you do not need to specify both.

    #[derive(Error, Debug)]
    pub struct MyError {
        msg: String,
        #[source]  // optional if field name is `source`
        source: anyhow::Error,
    }
  5. Use transparent errors with #[error(transparent)]

    master

    Use #[error(transparent)] to forward the source() and Display methods straight through to an underlying error. This is useful for:

    1. Enums acting as a wrapper for an 'anything else' variant.
    2. Hiding implementation details behind an opaque public error type.
    #[derive(Error, Debug)]
    pub enum MyError {
        #[error(transparent)]
        Other(#[from] anyhow::Error),
    }
    
    // Hiding implementation details
    #[derive(Error, Debug)]
    #[error(transparent)]
    pub struct PublicError(#[from] ErrorRepr);
    
    enum ErrorRepr {
        // ...
    }
  6. Generate From implementations with #[from]

    master

    Adding #[from] to a variant automatically generates a From<SourceError> implementation. The variant must not contain any fields other than the source error (and optionally a backtrace).

    #[derive(Error, Debug)]
    pub enum MyError {
        Io(#[from] io::Error),
        Glob(#[from] globset::Error),
    }
  7. Configure Display messages with #[error]

    master

    The #[error("...")] attribute generates the Display implementation. It supports field interpolation using the following shorthands:

    • #[error("{var}")]write!("{}", self.var)
    • #[error("{0}")]write!("{}", self.0)
    • #[error("{var:?}")]write!("{:?}", self.var)
    • #[error("{0:?}")]write!("{:?}", self.0)

    You can also use arbitrary expressions or named arguments. To refer to fields of a struct or enum within an expression, use .var for named fields and .0 for tuple fields.

    #[derive(Error, Debug)]
    pub enum Error {
        #[error("invalid rdo_lookahead_frames {0} (expected < {max})", max = i32::MAX)]
        InvalidLookahead(u32),
    }
    
    #[derive(Error, Debug)]
    pub enum Error {
        #[error("first letter must be lowercase but was {:?}", first_char(.0))]
        WrongCase(String),
        #[error("invalid index {idx}, expected at least {} and at most {}", .limits.lo, .limits.hi)]
        OutOfBounds { idx: usize, limits: Limits },
    }
  8. Use Backtraces in errors

    master

    The provide() method is implemented for any field with a type named Backtrace. This requires a nightly compiler with Rust version 1.73 or newer. If a field is both a source and marked #[backtrace], the provide() method is forwarded to the source so both layers share the same backtrace.

    use std::backtrace::Backtrace;
    
    #[derive(Error, Debug)]
    pub struct MyError {
        msg: String,
        backtrace: Backtrace,  // automatically detected
    }
    
    #[derive(Error, Debug)]
    pub enum MyError {
        Io {
            #[backtrace]
            source: io::Error,
        },
    }
  9. Handle Backtraces with `Backtrace` and `#[backtrace]`

    master

    If a field has the type std::backtrace::Backtrace, thiserror implements the provide() method to return it.

    • Automatic Detection: Any field named Backtrace is automatically detected.
    • Manual Marking: Use #[backtrace] to mark a field as a backtrace.
    • Chaining: If a field is both a source and marked #[backtrace], provide() is forwarded to the source so both layers share the same backtrace.
    • Capture in #[from]: For variants using #[from] that also contain a Backtrace field, a backtrace is captured during the From implementation.

    Note: Requires a nightly compiler with Rust version 1.73 or newer.

  10. Use `#[error(transparent)]` for error delegation

    master

    The #[error(transparent)] attribute forwards both the Display and source() methods directly to the underlying error. This is useful for:

    1. Creating 'anything else' variants in enums.
    2. Hiding implementation details behind an opaque public error type to maintain API stability.
    #[derive(Error, Debug)]
    #[error(transparent)]
    pub struct PublicError(#[from] ErrorRepr);
    
    #[derive(Error, Debug)]
    enum ErrorRepr {
        // ... internal details
    }
  11. Use the derive(Error) macro

    master

    The thiserror crate provides a derive(Error) procedural macro to simplify implementing the std::error::Error trait for custom error types.

    You can use the #[derive(Error)] attribute on your error types. The macro supports several helper attributes to define how the error is constructed and displayed:

    • #[error("...")]: Defines the display format for the error using a format string.
    • #[from]: Automatically implements From<T> for a field, allowing the error to be created from a wrapped error type.
    • #[source]: Marks a field as the underlying source of the error (the source() method of the Error trait).
    • #[backtrace]: Enables support for capturing and returning a backtrace.