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,
)
}
}
运行效果
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可以视情况选择。