rust从0开始写项目-04-多样化错误处理

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

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

Part1一、anyhow

anyhow::Error 是这个 crate 中最重要的结构体,它是动态错误类型的包装器,能从所有实现了 std::error::Error + Send + Sync + 'static 的错误转换而来,也能转换成 Box,它有以下特点:

  1. anyhow::Error 要求包裹的错误必须是 Send + Sync + 'static;
  2. anyhow::Error 保证 backtrace 是可用的,就是底层的错误类型没有提供;
  3. anyhow::Error 在内存中只占一个机器字而不是两个;

11.1 打印anyhow方式

  1. 可以使用 {} 或者 .to_string(),但是仅仅打印最外层错误或者上下文,而不是内层的错误;

  2. 可以使用 {:#} 打印外层和底层错误;

  3. 可以使用 {:?} 在调试模式打印错误以及调用栈;

  4. 可以使用 {:#?} 以结构体样式打印错误,例如:

    Error {
    context: "Failed to read instrs from ./path/to/instrs.json",
    source: Os {
    code: 2,
    kind: NotFound,
    message: "No such file or directory",
    },
    }

21.2 downcast_ref

anyhow 提供了 downcast_ref 方法,用于在运行时将 anyhow::Error 转换为其包含的具体错误类型的引用。这可以用于检查和处理特定类型的错误。

复制代码
use anyhow::{anyhow,Error};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum  MyError {
    #[error("the data for key `{0}` is not available")]
    Redaction(String)
}

pub fn anyhow_use(){
    let error: Error = anyhow!(MyError::Redaction("keys".to_string()));

    if let Some(my_error) = error.downcast_ref::<MyError>() {
        println!("MyError is parse ok: {}", my_error);
        // Handle MyError specifically
    } else {
        println!("Unknown error: {:?}", error);
        // Handle other types of errors
    }
}

MyError is parse ok: the data for key `keys` is not available

31.3 anyhow!

使用 anyhow! 这个宏可以生成 anyhow::Error类型的值,它可以接受字符串,格式化字符串作为参数,或者实现 std::error:Error 的错误作为参数。

复制代码
use anyhow::{anyhow,Error,Result};

fn anyhow()->Result<()>{

    return Err(anyhow!("this is return error"))
}
pub fn anyhow_use(){
    let err = anyhow();
    println!("{:#?}",err)
}

Err(
    "this is return error",
)

41.4 anyhow配合thiserror使用

项目更多用到的组合

复制代码
use anyhow::{anyhow, Error, Result};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    #[error("the data for key `{0}` is not available")]
    Redaction(String),
}

fn bar() -> std::result::Result<(), DataStoreError> {
    Err(DataStoreError::Redaction(
        "bar() std::result::Result".to_string(),
    ))
}

fn foo() -> anyhow::Result<()> {
    let a = bar()?;
    Ok(())
}

fn foo2() -> anyhow::Result<()> {
    Err(anyhow::Error::from(DataStoreError::Redaction(
        "foo2 (anyhow::Error::from(DataStoreError::Redaction ".to_string(),
    )))
}

fn foo3() -> anyhow::Result<()> {
    Err(anyhow!(DataStoreError::Redaction(
        "foo3 anyhow!(DataStoreError::Redaction".to_string()
    )))
}

fn foo4() -> anyhow::Result<()> {
    Err(anyhow!("foo4 anyhow! {}", "f4"))
}

pub fn anyhow_use() {
    // let err = anyhow();
    // println!("{:#?}",err)

    let f = foo();
    println!("foo {:?}", f);
    let f1 = foo2();
    println!("foo2 {:?}",f1);

    let f2 = foo3();
    println!("foo3 {:?}",f2);

    let f3 = foo4();
    println!("foo4 {:?}",f3);
}

foo Err(the data for key `bar() std::result::Result` is not available)
foo2 Err(the data for key `foo2 (anyhow::Error::from(DataStoreError::Redaction ` is not available)
foo3 Err(the data for key `foo3 anyhow!(DataStoreError::Redaction` is not available)
foo4 Err(foo4 anyhow! f4)

51.5 bail!

anyhow::bail 宏用于提前错误返回,它等价于 return Err(anyhow!($args...)),包含这个宏的函数的返回值必须是 Result<_,anyhow::Error>:

复制代码
use anyhow::{anyhow,bail, Error, Result};
use futures::future::err;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    #[error("the data for key `{0}` is not available")]
    Redaction(String),
}


fn bail(i:i16)->anyhow::Result<()>{

    if i < 16{
        bail!(DataStoreError::Redaction(i.to_string() + &" is less 16".to_string()))
    }
    Ok(())
}

直接返回

复制代码
bail! error Err(the data for key `1 is less 16` is not available)

61.6 anyhow::Context

anyhow::Context 为 anyhow::Result 类型提供了 context 方法,能在错误发生时提供更多的上下文信息:

复制代码
use anyhow::{anyhow, bail, Context, Error, Result};
use futures::future::err;
use std::fs;
use std::path::PathBuf;
use thiserror::Error;


pub struct ImportantThing {
    path: PathBuf,
}

impl ImportantThing {
    pub fn detach(&mut self) -> Result<()> {
        Err(anyhow!("detach failed"))
    }
}

pub fn do_it(mut it: &mut ImportantThing) -> Result<Vec<u8>> {
    it.detach()
        .context("Failed to detach the important thing")?;
    let path = &it.path;
    let context =
        fs::read(path).with_context(|| format!("Failed to read in str form {}", path.display()))?;
    Ok(context)
}

pub fn do_it1(it: &mut ImportantThing) -> Result<Vec<u8>> {
    let path = &it.path;
    let content =
        fs::read(path).with_context(|| format!("Failed to read instrs from {}", path.display()))?;

    Ok(content)
}
pub fn anyhow_use() {
    // let err = anyhow();
    // println!("{:#?}",err)

    // let f = foo();
    // println!("foo {:?}", f);
    // let f1 = foo2();
    // println!("foo2 {:?}",f1);
    //
    // let f2 = foo3();
    // println!("foo3 {:?}",f2);
    //
    // let f3 = foo4();
    // println!("foo4 {:?}",f3);

    // let b = bail(1);
    // println!("bail! error {:?}", b)

    let mut it = ImportantThing{
        path:PathBuf::new()
    };

    match do_it(&mut it) {
        Ok(_)=>(),
        Err(err)=>{
            for cause in err.chain(){
                println!("{}",cause)
            }
        }
    }

    match do_it1(&mut it) {
        Ok(_)=>(),
        Err(err)=>{
            for cause in err.chain(){
                println!("{}",cause)
            }
        }
    }

}

Failed to detach the important thing
detach failed

Failed to read instrs from 
No such file or directory (os error 2)

本文由mdnice多平台发布

相关推荐
FullStackGeek5 分钟前
微信缴费APIV3接入
后端
灯澜忆梦12 分钟前
【Go Zap 日志库】#1 |快速上手 + 配置详解
开发语言·后端·golang
m0_5873830017 分钟前
无人自助拼豆系统从需求分析到落地部署全流程实战开发指南
java·经验分享·后端·struts·小程序·架构·需求分析
Rain的Java大神之路27 分钟前
被问麻了!Function Calling、MCP、Skill 到底啥关系?一张图讲透三层架构,附 Java 生产落地全流程+30个坑
java·后端·ai编程
云和数据.ChenGuang1 小时前
用 Claude Code 重构遗留系统:从评估到落地的完整指南
java·人工智能·spring boot·后端·重构·springai
豆角焖肉2 小时前
SpringBoot 集成腾讯 AI 人脸识别
人工智能·spring boot·后端·人脸识别·腾讯 ai·base64 图片
付威20232 小时前
如果你想拥有自己的 Agent,为什么值得从 rpi 开始
后端
可乐ea2 小时前
2000 个智能体把 Agent 重写成 Rust:编排流水线拆解
开发语言·后端·rust·多智能体协作·ai编程助手·智能体编排·rust重写
用户72757483196502 小时前
02-变量、常量和字符串:len("Hello, 世界") 为什么不是 9?
后端
SamDeepThinking2 小时前
CyclicBarrier的底层实现
java·后端·面试