Tao
Tao

Guide to thiserror

This article will introduce and provide a demo based on version 1.0.59. The thiserror crate further simplifies the process of defining custom error types in Rust. It provides a convenient way to define custom error types for programs based on a derive procedural macro. Compared with manually implementing the std::error::Error trait, using thiserror can greatly reduce boilerplate code and provide better type safety and readability.

This is the core macro of thiserror, used to automatically derive the implementation of the std::error::Error trait for custom error types. It can only be used on enum and struct data structures.

rust

#[derive(Error, Debug)]
pub enum Error {
    #[error("invalid rdo_lookahead_frames {0} (expected < {})", i32::MAX)]
    InvalidLookahead(u32),
}

This attribute macro is used to define the display format for error variants. It accepts a formatted string that can contain placeholders {}. If a number is included in the placeholder, it will correspond to the same field index in that variant. It can only be used on struct fields or enum variants.

Supported formats:

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

rust

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,
}

This attribute can be applied to an error variant, making it a transparent error wrapper. This means that when the error is displayed, it will directly show the content of the wrapped error instead of displaying its own formatted string.

rust

#[derive(Error, Debug)]
pub enum MyError {
    ...

    #[error(transparent)]
    Other(#[from] anyhow::Error),  // source and Display delegate to anyhow::Error
}

This attribute can be applied to a single field, implementing the From trait, mainly used to convert a specified Error to the corresponding custom Error.

rust

#[derive(Error, Debug)]
pub enum MyError {
    ...
    #[error("unknown data store error")]
    Other(#[from] anyhow::Error),  // delegate anyhow::Error
}

This attribute can be applied to a single field to mark it as the error source. When displaying the error, the value of this field will be used as the error source instead of using the formatted string. If the field is source, it is marked with #[source] by default.

rust

#[derive(Error, Debug)]
pub struct MyError {
    msg: String,
    #[source]  // optional if field name is `source`
    source: anyhow::Error,
}

This attribute can be applied to error locations to enable adding backtrace information to struct or enum. In debug mode, the error message will include backtrace information, which is helpful for diagnosing errors. This macro is currently in nightly, and cannot be used in the stable version. It will cause a compilation error in the current version of Rust.

rust

use std::backtrace::Backtrace;

#[derive(Error, Debug)]
pub struct MyError {
    msg: String,
    backtrace: Backtrace,  // automatically detected
}

These macros provide a convenient way to define custom error types and also support advanced features such as error wrapping and backtrace information. Using thiserror can greatly simplify error handling code in Rust, improving code readability and maintainability. You can refer to the sample code for more details.