rust从0开始写项目-03-多样话错误处理

一个优秀的项目,错误处理的优雅性是至关重要的,而rust,anyhow creat是绕不过去的一个,今天我们来研究下,怎么使用它,帮助我们写出更优雅的代码

关注 vx golang技术实验室,获取更多golang、rust好文

Part1一、thiserror初体验

可以使用命令 cargo add thiserror 将它添加到自己的项目中,或者在 Cargo.toml 中添加如下的配置:

复制代码
[dependencies]
thiserror = "1.0"

thiserror 可以用于枚举或者结构体,例如,我们来看一个基本的例子:

复制代码
use std::io;
use log::error;
use thiserror::Error;
#[derive(Error,Debug)]
pub enum DataStoreError {
    #[error("data store disconnected")]
    Disconnect(#[from] std::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
}

pub fn error(){
    ///error
    println!("这是没有参数的 Unknown {}",DataStoreError::Unknown);
    println!("这是结构体参数的 InvalidHeader {}",DataStoreError::InvalidHeader {
        expected : String::from("expected"),
        found : String::from("found")
    });
    println!("这是有index参数的 Redaction {}",DataStoreError::Redaction(String::from("Redaction")));
    println!("这是有from参数的 Disconnect {}",DataStoreError::Disconnect(io::Error::from(io::ErrorKind::TimedOut)));

}

这是没有参数的 Unknown unknown data store error
这是结构体参数的 InvalidHeader invalid header (expected "expected",found "found")
这是有index参数的 Redaction the data for key `Redaction` is not available
这是有from参数的 Disconnect data store disconnected

然后我们来仔细分析下各种用法

Part2二、#[error]

如果使用 #[error(...)] 为结构体或者枚举生成自定义错误消息,这将为它们实现 Display

12.1 Enum

复制代码
#[derive(Debug)]
pub struct Limits{
    lo : i16,
    hi : i16
}

#[derive(Error,Debug)]
pub enum MyError{
    #[error("invalid rdo_lookahead_frames {0} (expected < {})",i8::MAX)]
    InvalidLookahead(u32),
    #[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}
}

pub fn error(){
    println!("这是 enum 的InvalidLookahead {}",MyError::InvalidLookahead(3333));
    //自动调用函数进行比较
    println!("这是 enum 的 WrongCase {}",MyError::WrongCase("kk".to_string()));
    println!("这是 enum 的 OutOfBounds {}",MyError::OutOfBounds{idx : 89,limits:Limits{
        lo:12,
        hi:11
    }});

}

这是 enum 的InvalidLookahead invalid rdo_lookahead_frames 3333 (expected < 127)
这是 enum 的 WrongCase first letter must be lowercase but was 'k'
这是 enum 的 OutOfBounds invalid index 89,expected at least 12 and at most 11

22.2 struct

复制代码
#[derive(Error, Debug)]
#[error("something failed, msg is: {msg}")]
pub struct MyErrorStruct {
    msg: &'static str,
}

println!("这是 struct 的msg  {}",MyErrorStruct{msg:"失败的msg"});


这是 struct 的msg  something failed, msg is: 失败的msg

32.3 其他结构

其他结构也是支持的,例如 tuple、空struct 等等

Part3三、#[from]

可以使用 #[from] 注解为错误类型实现 From,可以从其他错误生成:

复制代码
#[derive(Error, Debug)]
#[error("some io error happened, {:?}", .source)]
pub struct MyFromError {
    #[from]
    source: io::Error,
}

println!("这是 struct 的 from 的 {}",MyFromError::from(io::Error::from(io::ErrorKind::TimedOut)));

这是 struct 的 from 的 some io error happened, Kind(TimedOut)

Part4四、#[source]

可以使用 #[source] 属性,或者将字段命名为 source,可为自定义错误实现 source 方法,返回底层的错误类型:

复制代码
use std::error::Error;
use std::io;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum MyError {
  #[error("some io error happened, {:?}", .source)]
  IO { source: io::Error },
}

fn main() {
  let err = MyError::IO {
      source: io::Error::from(io::ErrorKind::TimedOut),
  };
  println!("{:?}", err.source());
}

或者使用 #[source] 属性标记非 source 的字段,例如:这里是 err 字段:

复制代码
use std::error::Error;
use std::io;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum MyError {
  #[error("some io error happened, {:?}", .err)]
  IO {
      #[source]
      err: io::Error,
  },
}

fn main() {
  let err = MyError::IO {
      err: io::Error::from(io::ErrorKind::TimedOut),
  };
  println!("{:?}", err.source());
}

#[from]#[source] 二选一即可,#[from] 也会为类型生成 .source() 方法,例如:

复制代码
#![allow(unused)]
#![feature(backtrace)]

use std::backtrace;
use std::error::Error as _;
use std::io;
use thiserror::Error;

#[derive(Error, Debug)]
#[error("some io error happened, {:?}", .source)]
pub struct MyError {
  #[from]
  source: io::Error,
  backtrace: backtrace::Backtrace,
}

fn main() {
  let err = MyError::from(io::Error::from(io::ErrorKind::TimedOut));
  println!("{:?}", err.source());
}

Part5五、#[backtrace]

只要在我们的错误结构体里面放个类型为 std::backtrace::Backtrace 的字段,就会自动实现 backtrace() 方法,可以看 #[from]

另外,如果使用 #[backtrace] 标记 sourcesource 字段,或者 #[source],或者 #[from]),那么 backtrace() 方法会转发到 sourcebacktrace

Part6六、#[error(transparent)]

可以通过 #[error(transparent)]sourceDisplay 直接使用底层的错误,这对于那些想处理任何的类型来说是很有用的:

复制代码
use std::io;
use log::error;
use thiserror::Error;
use anyhow::anyhow;
use std::error::Error as _;
#[derive(Error,

#[derive(Error, Debug)]
#[error(transparent)]
pub struct MyErrorTrans {
    #[from]
    source: anyhow::Error,
}

#[derive(Error, Debug)]
pub enum MyErrorTransEnum {
    #[error("file not found")]
    FileNotFound,
    #[error(transparent)]
    Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
}

//transparent
let err = MyErrorTrans::from(anyhow!("Missing attribute: {}", "field1"));
println!("{}", err);
println!("{:?}", err);

let err = MyErrorTransEnum::from(anyhow!("Missing attribute: {}", "field1"));
println!("{}", err);
println!("{:?}", err);

Missing attribute: field1
MyErrorTrans { source: Missing attribute: field1 }
Missing attribute: field1
Other(Missing attribute: field1)

本文由mdnice多平台发布

相关推荐
5***84648 分钟前
Spring全家桶简介
java·后端·spring
苏三的开发日记18 分钟前
minio服务集群搭建
后端
开心猴爷19 分钟前
苹果iOS应用上架App Store必看指南与规则
后端
哈哈哈笑什么25 分钟前
解决微服务系统中跨服务的超卖、库存锁定不释放、消息丢失、重复扣减库存等核心问题
后端
晨非辰30 分钟前
算法闯关日记 Episode :解锁链表「环形」迷局与「相交」奥秘
数据结构·c++·人工智能·后端·python·深度学习·神经网络
小周在成长30 分钟前
Java 权限修饰符(Access Modifiers)指南
后端
00后程序员33 分钟前
iOS 上架 4.3,重复 App 审核条款的真实逻辑与团队应对策略研究
后端
00后程序员42 分钟前
专业的 IPA 处理工具 构建可维护、可回滚的 iOS 成品加工与加固流水线
后端
百度Geek说1 小时前
项目级效能提升一站式交付最佳实践
后端
今天你TLE了吗1 小时前
通过RocketMQ延时消息实现优惠券等业务MySQL当中定时自动过期
java·spring boot·后端·学习·rocketmq