• Rust 错误处理


    rust 处理错误,不使用 try catch, 而是使用 Result

    简单的处理rust错误

    在各种关于rust错误处理的文档中,为了解释清楚其背后的机制,看着内容很多,不好理解。

    比如我们写一个方法,读取文件内容:

    fn read_file_to_string(file_path: String) -> Result<String, io::Error>{
        let mut file = File::open(file_path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(contents)
    }

    上面的代码,当文件不存在的时候,也可以很好的返回异常信息。

    调用代码:

        let r = read_file_to_string(r"d:\1111.txt".to_string());
        match r {
            Ok(str) => println!("OK: {str}"),
            Err(e) => println!("Error: {e}"),
        };

    如果文件不存在,会输出信息:

    这个异常处理的过程不复杂,分为三步:

    1. 自定义的函数要返回Result

    2. 返回Result的函数时,后面加上问号,

    3. 在最上层,使用match处理结果。

    但是这样是不够的,如果在一个大项目中,我们很难找到是哪个文件缺失了,rust不像c#那样,可以很容易的获取到出现问题的代码行数、类和方法名等。

    最直观的方法是,在异常信息里,带上文件名。

    自定义错误,带上文件名

    rust自定义错误分为三步:

    1)定义错误类型

    2)实现Error特征(trait)

    3)  实现Display特征

    自定义错误的类型是enum, 和其他语言相比,这有点奇怪。 代码如下:

    // 定义自定义错误类型
    #[derive(Debug)]
    pub enum MyError {
        FileOpenError(String),
        ParseError(String),
        Common(String),
    }
    
    // 实现Error特质
    impl Error for MyError {}
    
    // 实现Display特质以便打印错误信息
    impl fmt::Display for MyError {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                MyError::FileOpenError(msg) => write!(f, "Failed to open file: {}", msg),
                MyError::ParseError(msg) => write!(f, "Parse error: {}", msg),
                MyError::Common(msg) => write!(f, "Other error: {}", msg),
            }
        }
    }

    这时,读取文件的函数代码要改成这样:

    fn read_file_to_string(file_path: String) -> Result<String, MyError>{
        let r = File::open(file_path.clone());
        match r {
            Ok(mut file) => {
                let mut contents = String::new();
                let r2 = file.read_to_string(&mut contents);
                match r2 {
                    Ok(size) => return Ok( contents),
                    Err(e) => return Err(MyError::Common(format!("{e} 文件: {file_path}"))),
                } 
            },
            Err(e) => {
                return Err(MyError::FileOpenError(format!("{e} 文件: {file_path}")));
            },
        }
    }

    代码变得很啰嗦,好在能比较好的显示错误了:

    自定义错误的三部曲,虽然有点长,但是这是项目的公共代码,还是可以忍受的。读取文件的代码,和 c#比起来,真的太罗嗦了。

    简化通用异常处理

    读取文件内容的函数,代码罗嗦的原因是,异常类型通过问号匹配到自定义的MyError很麻烦。

    这里我们采用一种更通用的方式,来处理异常:

    1) 重新定义异常类型,并且提供其他异常向自定义异常转换的方法

    custom_error.rs:

    use std::error::Error;
    use std::fmt;
    use std::fmt::Display;
    
    // 自定义错误类型,包含文件路径信息
    #[derive(Debug)]
    pub struct MyError {
        msg: String,
        source: String,
    }
    
    // 为自定义错误类型实现Error trait
    impl Error for MyError {}
    
    // 实现Display trait,以便于打印错误信息
    impl fmt::Display for MyError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{:?}: {}", self.source, self.msg)
        }
    }
    
    pub fn convert_error(msg:String, err: String) -> MyError {
        MyError {
            msg: msg ,
            source: err.to_string(),
        }
    }
    
    // 定义一个新的trait
    pub trait MyErrorExtension {
        fn ex_err(self, msg:&String)->  Result;
    }
    
    // 为Result类型实现MyExtension trait
    impl MyErrorExtension for Result {
        fn ex_err(self, msg:&String) ->  Result {
            match self {
                Ok(t) => Ok(t),
                Err(e) => Err(MyError{msg:msg.to_string(), source: e.to_string()}),
            }
        }
    }

    2) 定义带有通用异常处理能力的函数的示例:

    fn read_file_to_string(file_path: String) -> Result<String, MyError>{
        let context_info = format!("文件路径: {file_path}");
        fs::metadata(&file_path).ex_err(&context_info)?;
        let mut file = File::open(&file_path).ex_err( &context_info)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents).ex_err(&context_info)?;
        Ok(contents)
    }

    以打开文件的方法为例,原本的调用是:

    let mut file = File::open(&file_path)?;

    新的调用,后面附加了重要的上下文信息,并且把异常类型转换为MyError:

    let mut file = File::open(&file_path).ex_err( &context_info)?;

    通过扩展方法ex_err, 达到了我们的目的。

  • 相关阅读:
    CSAPP Lab6:Malloc
    多线程---进阶
    EM@函数奇偶性性质@函数四则运算和复合运算后的奇偶性判断
    spring boot+redis 的快速入门
    学生python编程----飞机训练
    LVS之DR模式(最常见的LVS负载方式,直接路由模式)
    Ceph RBD 的实现原理与常规操作
    第3章-线性方程组(3)
    47. 从零开始学springboot: spel结合redisson实现动态参数分布式锁
    时区的问题
  • 原文地址:https://www.cnblogs.com/ybst/p/18182038