rust 错误处理-kb88凯时官网登录

来自:
时间:2024-05-10
阅读:
免费资源网 - https://freexyz.cn/

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

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

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

fn read_file_to_string(file_path: string) -> result{
    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}"),
    };

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

rust 错误处理

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

  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{
    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{
    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, 达到了我们的目的。

免费资源网 - https://freexyz.cn/
返回顶部
顶部
网站地图