导航
- 一、背景
- 二、实践
- 1、导入thiserror
- 2、自定义错误消息体
- (1)创建ErrMsg.rs和创建自定义结构体
- (2)lib.rs添加ErrMsg
- (3)main函数
- (4)完整代码
一、背景
开发中遇到需要通用、能够满足自定义 的错误输出方式,
thiserror是一个不错的工具包,在此基础上我还想加一些自定义的错误消息体,比如HTTP类的错误信息,打印状态码和错误内容
二、实践
1、导入thiserror
thiserror = "1.0"
2、自定义错误消息体
(1)创建ErrMsg.rs和创建自定义结构体
若想能够打印错误信息,为自定义结构体实现display和Debug特征,看完整代码
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
// src/lib.rs
pub mod ErrBean;
(3)main函数
使用thiserror
定义枚举,枚举应该包括你需要的所有Error
类型
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
函数
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);
}
}
输出结果为
this is display trait output :ErrMsg: {code:400,msg :自定义错误内容 }%
(4)完整代码
为了保证模块化开发,关于错误实体的定义和方法实现应该都放到一个rs文件中,别的文件需要使用时直接导入就好,所以ErrMsg.rs
用来定义我们的thiserror
和自定义错误消息结构体
ErrMsg.rs
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
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);
}
}