Rust错误处理

虽然标准库定义了大量的错误类型,但是一个严谨的项目,光使用这些错误类型往往是不够的,例如我们可能会为暴露给用户的错误定义相应的类型。


//自定义错误类型只需要实现 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

Comments
登录后评论
Sign In