虽然标准库定义了大量的错误类型,但是一个严谨的项目,光使用这些错误类型往往是不够的,例如我们可能会为暴露给用户的错误定义相应的类型。
//自定义错误类型只需要实现 Debug 和 Display 特征即可,source 方法是可选的,而 Debug 特征往往也无需手动实现,可以直接通过 derive 来派生
use std::fmt;
// AppError 是自定义错误类型,它可以是当前包中定义的任何类型,在这里为了简化,我们使用了单元结构体作为例子。
// 为 AppError 自动派生 Debug 特征
#[derive(Debug)]
struct AppError;
// 为 AppError 实现 std::fmt::Display 特征
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "An Error Occurred, Please Try Again!") // user-facing output
}
}
// 一个示例函数用于产生 AppError 错误
fn produce_error() -> Result<(), AppError> {
Err(AppError)
}
fn main(){
match produce_error() {
Err(e) => eprintln!("{}", e),
_ => println!("No error"),
}
eprintln!("{:?}", produce_error()); // Err({ file: src/main.rs, line: 17 })
}
#[cfg(test)]
mod test {
// 问题就来了,我们该如何将其它的错误类型转换成自定义的错误类型?
#[test]
fn test_from()->Result<(), std::io::Error> {
use std::fs::File;
use std::io;
#[derive(Debug)]
struct AppError {
kind: String, // 错误类型
message: String, // 错误信息
}
// 为 AppError 实现 std::convert::From 特征,由于 From 包含在 std::prelude 中,因此可以直接简化引入。
// 实现 From<io::Error> 意味着我们可以将 io::Error 错误转换成自定义的 AppError 错误
impl From<io::Error> for AppError {
fn from(error: io::Error) -> Self {
AppError {
kind: String::from("io"),
message: error.to_string(),
}
}
}
//let _file = File::open("nonexistent_file.txt")?; // Error: AppError { kind: "io", message: "No such file or directory (os error 2)" }
Ok(())
}
//归一化不同的错误类型
//如果函数中的两个 ? 返回的实际上是不同的错误:如env::var() 返回的是 std::env::VarError,而 read_to_string 返回的是 std::io::Error。
//为了满足 render 函数的签名,我们就需要将 env::VarError 和 io::Error 归一化为同一种错误类型。要实现这个目的有三种方式:
// 1、使用特征对象 Box<dyn Error>
// 2、自定义错误类型
// 3、使用 thiserror
use std::error::Error;
// Box<dyn Error>
#[test]
fn test_one()-> Result<(), Box<dyn Error>> {
use std::fs::read_to_string;
fn render() -> Result<String, Box<dyn Error>> {
let file = std::env::var("MARKDOWN")?;
let source = read_to_string(file)?;
Ok(source)
}
let html = render()?;
println!("{}", html);
Ok(())
}
// 自定义错误类型
#[test]
fn test_custom() {
fn render() -> Result<String, MyError> {
let file = std::env::var("MARKDOWN")?;
let source = read_to_string(file)?;
Ok(source)
}
#[derive(Debug)]
enum MyError {
EnvironmentVariableNotFound,
IOError(std::io::Error),
}
impl From<std::env::VarError> for MyError {
fn from(_: std::env::VarError) -> Self {
Self::EnvironmentVariableNotFound
}
}
impl From<std::io::Error> for MyError {
fn from(value: std::io::Error) -> Self {
Self::IOError(value)
}
}
impl std::error::Error for MyError {}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MyError::EnvironmentVariableNotFound => write!(f, "Environment variable not found"),
MyError::IOError(err) => write!(f, "IO Error: {}", err.to_string()),
}
}
}
// let html = render()?;
// println!("{}", html);
}
// thiserror可以帮助我们简化上面的第二种解决方案:
#[test]
fn test_thieerr() {
fn render() -> Result<String, MyError> {
let file = std::env::var("MARKDOWN")?;
let source = read_to_string(file)?;
Ok(source)
}
#[derive(thiserror::Error, Debug)]
enum MyError {
#[error("Environment variable not found")]
EnvironmentVariableNotFound(#[from] std::env::VarError),
#[error(transparent)]
IOError(#[from] std::io::Error),
}
//let html = render()?;
//pintln!("{}", html);
//Ok(())
}
use anyhow::Result;
use std::fs::read_to_string;
//关于如何选用 thiserror 和 anyhow 只需要遵循一个原则即可:是否关注自定义错误消息,关注则使用 thiserror(常见业务代码),否则使用 anyhow(编写第三方库代码)
#[test]
fn test_how() -> Result<()>{
fn render() -> Result<String> {
let file = std::env::var("MARKDOWN")?;
let source = read_to_string(file)?;
Ok(source)
}
let html = render()?;
println!("{}", html);
Ok(())
}
}
详细文章见:https://blog.csdn.net/Unicorn_wan/article/details/125291852