Crate thiserror

This library provides a convenient derive macro for the standard library’s std::error::Error trait.

提供了定义标准错误的过程宏,减少了手写模板代码量,直接上代码

use std::error::Error;
use std::io;
use thiserror::Error;

fn main() {
    let error1 = CustomEnumError::from(io::Error::from(io::ErrorKind::PermissionDenied));
    let error2 = CustomStructError {
        msg: "read error".to_string(),
        source: CustomEnumError::from(io::Error::from(io::ErrorKind::InvalidData)),
    };
    println!("{}", error1);
    println!("{},caused by {:?}",error2,error2.source());
}

#[derive(Error, Debug)]
enum CustomEnumError {
    #[error("the source {0} is not found")] //error宏的实质是实现Display
    Notfound(String),

    #[error(transparent)] //from宏会为该枚举变体实现From trait,实现了错误类型的转换,transparent会直接调用错误源的display方法
    IO(#[from] std::io::Error),
}

#[derive(Error, Debug)]
#[error("{msg}")] //为该结构体实现Display
pub struct CustomStructError {
    msg: String,
    #[source] // source宏会为该字段实现std::error::Error trait中的source方法
    source: CustomEnumError,
}

看看宏展开后的代码

use std::error::Error;
use std::io;
use thiserror::Error;
fn main() {
    let error1 = CustomEnumError::from(io::Error::from(io::ErrorKind::PermissionDenied));
    let error2 = CustomStructError {
        msg: "read error".to_string(),
        source: CustomEnumError::from(io::Error::from(io::ErrorKind::InvalidData)),
    };
    {
        ::std::io::_print(format_args!("{0}\n", error1));
    };
    {
        ::std::io::_print(
            format_args!("{0},caused by {1:?}\n", error2, error2.source()),
        );
    };
}
enum CustomEnumError {
    #[error("the source {0} is not found")]
    Notfound(String),
    #[error(transparent)]
    IO(#[from] std::io::Error),
}
#[allow(unused_qualifications)]
#[automatically_derived]
impl ::thiserror::__private::Error for CustomEnumError {
    fn source(
        &self,
    ) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> {
        use ::thiserror::__private::AsDynError as _;
        #[allow(deprecated)]
        match self {
            CustomEnumError::Notfound { .. } => ::core::option::Option::None,
            CustomEnumError::IO { 0: transparent } => {
                ::thiserror::__private::Error::source(transparent.as_dyn_error())
            }
        }
    }
}
#[allow(unused_qualifications)]
#[automatically_derived]
impl ::core::fmt::Display for CustomEnumError {
    fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        use ::thiserror::__private::AsDisplay as _;
        #[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
        match self {
            CustomEnumError::Notfound(_0) => {
                match (_0.as_display(),) {
                    (__display0,) => {
                        __formatter
                            .write_fmt(
                                format_args!("the source {0} is not found", __display0),
                            )
                    }
                }
            }
            CustomEnumError::IO(_0) => ::core::fmt::Display::fmt(_0, __formatter),
        }
    }
}
#[allow(deprecated, unused_qualifications, clippy::needless_lifetimes)]
#[automatically_derived]
impl ::core::convert::From<std::io::Error> for CustomEnumError {
    fn from(source: std::io::Error) -> Self {
        CustomEnumError::IO { 0: source }
    }
}
#[automatically_derived]
impl ::core::fmt::Debug for CustomEnumError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            CustomEnumError::Notfound(__self_0) => {
                ::core::fmt::Formatter::debug_tuple_field1_finish(
                    f,
                    "Notfound",
                    &__self_0,
                )
            }
            CustomEnumError::IO(__self_0) => {
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "IO", &__self_0)
            }
        }
    }
}
#[error("{msg}")]
pub struct CustomStructError {
    msg: String,
    #[source]
    source: CustomEnumError,
}
#[allow(unused_qualifications)]
#[automatically_derived]
impl ::thiserror::__private::Error for CustomStructError {
    fn source(
        &self,
    ) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> {
        use ::thiserror::__private::AsDynError as _;
        ::core::option::Option::Some(self.source.as_dyn_error())
    }
}
#[allow(unused_qualifications)]
#[automatically_derived]
impl ::core::fmt::Display for CustomStructError {
    #[allow(clippy::used_underscore_binding)]
    fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        use ::thiserror::__private::AsDisplay as _;
        #[allow(unused_variables, deprecated)]
        let Self { msg, source } = self;
        match (msg.as_display(),) {
            (__display_msg,) => __formatter.write_fmt(format_args!("{0}", __display_msg)),
        }
    }
}
#[automatically_derived]
impl ::core::fmt::Debug for CustomStructError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(
            f,
            "CustomStructError",
            "msg",
            &self.msg,
            "source",
            &&self.source,
        )
    }
}

运行效果

image-20250106180109455

Crate anyhow

anyhow借助trait对象实现了错误类型的擦除,让我们在编辑逻辑代码时不必关注错误类型而是关注逻辑实现。当然,不关注不代表不处理,我们可以将任何实现了std::error::Error的错误向上传递并在某一结点统一做错误处理。为了更好的理解,看下面一段伪代码

fn get_res()->Result<(),MyError>{ // 返回的错误类型必须为MyError类型
	let res1 = method1()?; // method1方法的返回类型必须为Result<(),MyError>
	let res2 = method2()?; // method2同理,假设method为第三方库的方法调用,我们就必须为该方法的返回值做类型转换,这无疑时愚蠢的
	let res3 = method3()?;
	...
	Ok(())
}

使用anyhow做类型擦除

fn get_res()->Result<(),anyhow::Error>{ // anyhow::Error为实现了std::error::Error的trait对象
	let res1 = method1()?; // method1方法的返回类型只要实现了std::error::Error就会自动转换为anyhow::Error
	let res2 = method2()?; // method2同理,假设method为第三方库的方法调用,我们无需关注错误类型,当需要的时候做处理即可
	let res3 = method3()?;
	...
	Ok(())
}

此外anyhow还提供了两个宏

  • anyhow::bail! 宏允许你在函数中立即返回一个错误,并且它会自动将任何实现了 std::error::Error + 'static 的类型转换为 anyhow::Error
  • anyhow::anyhow! 宏可以创建一个新的 anyhow::Error,并将其作为 Result 的错误部分返回,支持插值。

最后再说两个crate,分别是eyre和color_eyre,eyre是基于anyhow编写的,因此和使用上和anyhow较为相似,它比anyhow提供了更友好的错误报告,但加入了更多的依赖,color_eyre则是进一步强化了友好的错误报告并支持彩色字体输出,具体选择哪个crate可以视情况选择。