Rust错误处理

这里不复述文档和教程中有的内容, 比如Result之类的细节, 可以自行看文档内容:

参考资料: https://www.philipdaniels.com/blog/2019/defining-rust-error-types/

1. Rust中自定义Error的方式

1.1 定义一个错误类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#[derive(Debug)]
pub enum MyErorType {
// Errors from external libraries..
Io(io::Error),
Git(git2::Error),
// Errors raised by us...
Regular(ErrorKind),
Custom(String)
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ErrorKind {
NotFound,
NotAuthorized,
// ...
}

impl ErrorKind {
fn as_str(&self) -> &str {
match *self {
ErrorKind::NotFound => "not found",
ErrorKind::NotAuthorized => "not authorized"
}
}
}

1.2 实现ErrorDisplay这两个trait

Error只允许返回静态字符串常量

1
2
3
4
5
6
7
8
9
10
impl Error for MyErrorType {
fn description(&self) -> &str {
match *self {
MyErrorType::Io(ref err) => err.description(),
MyErrorType::Git(ref err) => err.description(),
MyErrorType::Regular(ref err) => err.as_str(),
MyErrorType::Custom(ref err) => err,
}
}
}

fmt::Display更为灵活一点

1
2
3
4
5
6
7
8
9
10
impl fmt::Display for MyErrorType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
MyErrorType::Io(ref err) => err.fmt(f),
MyErrorType::Git(ref err) => err.fmt(f),
MyErrorType::Regular(ref err) => write!(f, "A regular error occurred {:?}", err),
MyErrorType::Custom(ref err) => write!(f, "A custom error occurred {:?}", err),
}
}
}

1.3 为external error type实现From

1
2
3
4
5
6
7
8
9
10
11
impl From<io::Error> for MyErrorType {
fn from(err: io::Error) -> MyErrorType {
MyErrorType::Io(err)
}
}

impl From<io::Error> for MyErrorType {
fn from(err: git2::Error) -> MyErrorType {
MyErrorType::Git(err)
}
}

使用起来就可以这样, 在捕获异常的代码片段中:

1
MyErrorType::From(err)

1.4 可选: 创建一个Result别名

1
pub type Result<T> = std::result::Result<T, MyErrorType>;

1.5 使用自定义的类型:

1
2
3
4
5
6
7
8
9
fn some_func() -> Result<usize> {
// possible: std::io::Error
let _f = std::fs::File::create("aa")?;
// possible: git2::Error
let _g = Respository::init("/path/to/repo")?;

// return a error
Err(MyErrorType::Regular(ErrorKind::NotAuthorized));
}

2. Rust 中使用 thiserroranyhow (简洁, 好用)

thiserror文档: https://docs.rs/thiserror/latest/thiserror/

anyhow文档: https://github.com/dtolnay/anyhow

这两个库主要还是为了某些库里处理数量众多的异常类型, 可以解决兼容, 但还需要额外处理

1
2
3
4
5
6
use anyhow::Result

fn get() -> Result<()> { // ~~接收任意类型异常~~

}

3. 错误类型映射

前面所作的努力只是为了兼容, 但是rust并不会主动去做, 转换工作还是得自己来

3.1 map_err

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use std::env;

fn double_arg(mut argv: env::Args) -> Result<i32, String> {
argv.nth(1)
.ok_or("Please give at least one argument".to_owned())
.and_then(|arg| arg.parse::<i32>().map_err(|err| err.to_string()))
.map(|n| 2 * n)
}

fn main() {
match double_arg(env::args()) {
Ok(n) => println!("{}", n),
Err(err) => println!("Error: {}", err),
}
}
1
2
3
4
5
// 这里是用构造函数做lambda函数了
cmdline.insert_str(boot_args)
.map_err(BootSourceConfigError::InvalidKernelCommandLine)
.map_err(VmmActionError::BootSource)
.map_err(CLIError::BootSource)?;

Rust错误处理
https://www.torch-fan.site/2022/08/02/Rust错误处理/
作者
Torch-Fan
发布于
2022年8月2日
更新于
2022年11月15日
许可协议