Rust 实战thiserror+自定义错误消息体

导航

一、背景

开发中遇到需要通用 、能够满足自定义 的错误输出方式,

thiserror是一个不错的工具包,在此基础上我还想加一些自定义的错误消息体,比如HTTP类的错误信息,打印状态码和错误内容

二、实践

1、导入thiserror

rust 复制代码
thiserror = "1.0"

2、自定义错误消息体

(1)创建ErrMsg.rs和创建自定义结构体

若想能够打印错误信息,为自定义结构体实现display和Debug特征,看完整代码

rust 复制代码
use crate::Display;
use std::error::Error;
use std::fmt::Debug;
use std::fmt::{self};
// ErrMsg 是自定义错误类型,
// 为 ErrMsg 自动派生 Debug 特征
#[derive(Debug)]
pub struct ErrMsg {
    pub code: u32,
    pub msg: String,
}

// 为 AppError 实现 std::fmt::Display 特征
impl fmt::Display for ErrMsg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ErrMsg: {{code:{},msg :{} }}", self.code, self.msg) // user-facing output
    }
}

pub fn print_err<E>(e: E)
where
    E: Display + Debug + Error,
{
    print!("{}", e);
}

(2)lib.rs添加ErrMsg

rust 复制代码
// src/lib.rs
pub mod ErrBean;

(3)main函数

使用thiserror 定义枚举,枚举应该包括你需要的所有 Error类型

rust 复制代码
enum MyError {
    #[error("invalid argument: {0}")]
    InvalidArgument(String),
    #[error("io error: {0}")]
    IoError(#[from] std::io::Error),
    #[error("this is display trait  output  :{0}")]//ErrMsg的display的输出,会替换这里的{0}
    ErrMsg(ErrMsg),
}

完整main函数

rust 复制代码
use hello_package::ErrBean::*;
use std::fmt::{self};
use std::fs::File;
use std::io::{self, Read};
use thiserror::Error;
#[derive(Error, Debug)]
enum MyError {
    #[error("invalid argument: {0}")]
    InvalidArgument(String),
    #[error("io error: {0}")]
    IoError(#[from] std::io::Error),
    #[error("this is display trait  output  :{0}")]
    ErrMsg(ErrMsg),
}
fn read_username_from_file() -> Result<String, MyError> {
    // 打开文件,f是`Result<文件句柄,io::Error>`
    let f = File::open("hello.txt");

    let mut f = match f {
        // 打开文件成功,将file句柄赋值给f
        Ok(file) => file,
        // 打开文件失败,将错误返回(向上传播)
        Err(e) => return Err(MyError::IoError(e)),
    };
    // 创建动态字符串s
    let mut s = String::new();
    // 从f文件句柄读取数据并写入s中
    match f.read_to_string(&mut s) {
        // 读取成功,返回Ok封装的字符串
        Ok(_) => Ok(s),
        // 将错误向上传播
        Err(e) => return Err(MyError::IoError(e)),
    }
}
fn produce_error() -> Result<String, MyError> {
    let err_msg = ErrMsg {
        code: 400,
        msg: String::from("自定义错误内容"),
    };
    Err(MyError::ErrMsg(err_msg))
}

fn main() {
    let res = produce_error();//可以替换成read_username_from_file
    if let Ok(e) = res {
        print!("{}", e);
    } else if let Err(e) = res {
        print!("{}", e);
    }
}

输出结果为

bash 复制代码
this is display trait  output  :ErrMsg: {code:400,msg :自定义错误内容 }%   

(4)完整代码

为了保证模块化开发,关于错误实体的定义和方法实现应该都放到一个rs文件中,别的文件需要使用时直接导入就好,所以ErrMsg.rs用来定义我们的thiserror和自定义错误消息结构体
ErrMsg.rs

rust 复制代码
use core::fmt::Display;
use std::error::Error;
use std::fmt::Debug;
use std::fmt::{self};

use thiserror::Error;
#[derive(Error, Debug)]
pub enum MyError {
  #[error("invalid argument: {0}")]//{0}是占位符号,原本error的打印信息会替换这个占位符
  InvalidArgument(String),
  #[error("io error: {0}")]
  IoError(#[from] std::io::Error),
  #[error("this is display trait  output  :{0}")]
  ErrMsg(ErrMsg),
}

// ErrMsg 是自定义错误类型,它可以是当前包中定义的任何类型,在这里为了简化,我们使用了单元结构体作为例子。
// 为 ErrMsg 自动派生 Debug 特征
#[derive(Debug)]
pub struct ErrMsg {
    pub code: u32,
    pub msg: String,
}

// 为 ErrMsg 实现 std::fmt::Display 特征
impl fmt::Display for ErrMsg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ErrMsg: {{code:{},msg :{} }}", self.code, self.msg) // user-facing output
    }
}

pub fn print_err<E>(e: E)
where
    E: Display + Debug + Error,
{
    print!("{}", e);
}

main.rs

rust 复制代码
use hello_package::ErrBean::*;
use std::fmt::{self};
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, MyError> {
    // 打开文件,f是`Result<文件句柄,io::Error>`
    let f = File::open("hello.txt");

    let mut f = match f {
        // 打开文件成功,将file句柄赋值给f
        Ok(file) => file,
        // 打开文件失败,将错误返回(向上传播)
        Err(e) => return Err(MyError::IoError(e)),
    };
    // 创建动态字符串s
    let mut s = String::new();
    // 从f文件句柄读取数据并写入s中
    match f.read_to_string(&mut s) {
        // 读取成功,返回Ok封装的字符串
        Ok(_) => Ok(s),
        // 将错误向上传播
        Err(e) => return Err(MyError::IoError(e)),
    }
}
fn produce_error() -> Result<String, MyError> {
    let err_msg = ErrMsg {
        code: 400,
        msg: String::from("自定义错误内容"),
    };
    Err(MyError::ErrMsg(err_msg))
}

fn main() {
    let res = produce_error();
    if let Ok(e) = res {
        print!("{}", e);
    } else if let Err(e) = res {
        print!("{}", e);
    }
}
相关推荐
风象南24 分钟前
SpringBoot的5种签到打卡实现方案
java·spring boot·后端
追逐时光者27 分钟前
C#/.NET/.NET Core技术前沿周刊 | 第 41 期(2025年6.1-6.8)
后端·.net
追逐时光者33 分钟前
不写一行代码 .NET 使用 FluentCMS 快速构建现代化内容管理系统(CMS)
后端·.net·cms
星辰离彬1 小时前
Java 高级泛型实战:8 个场景化编程技巧
java·开发语言·后端·程序人生
烛阴9 小时前
bignumber.js深度解析:驾驭任意精度计算的终极武器
前端·javascript·后端
服务端技术栈10 小时前
电商营销系统中的幂等性设计:从抽奖积分发放谈起
后端
你的人类朋友10 小时前
✍️Node.js CMS框架概述:Directus与Strapi详解
javascript·后端·node.js
面朝大海,春不暖,花不开10 小时前
自定义Spring Boot Starter的全面指南
java·spring boot·后端
钡铼技术ARM工业边缘计算机11 小时前
【成本降40%·性能翻倍】RK3588边缘控制器在安防联动系统的升级路径
后端
CryptoPP11 小时前
使用WebSocket实时获取印度股票数据源(无调用次数限制)实战
后端·python·websocket·网络协议·区块链