Rust系统编程之错误处理

错误处理

1. pacnic

主动退出,类似exit(0),但是可以携带信息。

1
2
3
4
5
6
7
8
9
10
fn give_princess(gift: &str) {
// 公主讨厌蛇,所以如果公主表示厌恶的话我们要停止!
if gift == "snake" { panic!("AAAaaaaa!!!!"); }
println!("I love {}s!!!!!", gift);
}

fn main() {
give_princess("teddy bear");
give_princess("snake");
}

2. unwrap

隐士解构Reuslt或Option。

a.unwrap()

  • unwrap作用于Option时,如果为0就panic,否则解构出值。

  • unwrap作用于Result时,如果为error就panic,否则解构出值。

1
2
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.unwrap(), 2);

b.unwrap_or_default()

  • unwrap_or_default作用于Option时,如果为0就返回值的默认值,否则解构出实际值。

  • unwrap_or_default作用于Result时,如果为error就返回值的默认值,否则解构出实际值。

1
2
3
4
5
6
7
let good_year_from_input = "1909";
let bad_year_from_input = "190blarg";
let good_year = good_year_from_input.parse().unwrap_or_default();
let bad_year = bad_year_from_input.parse().unwrap_or_default();

assert_eq!(1909, good_year);
assert_eq!(0, bad_year);

c.unwrap_or_else()

  • 不支持Option。

  • unwrap_or_else作用于Result时,如果为error就返回从闭包中计算得到的值,否则解构出实际值。

1
2
3
4
fn count(x: &str) -> usize { x.len() }

assert_eq!(Ok(2).unwrap_or_else(count), 2);
assert_eq!(Err("foo").unwrap_or_else(count), 3);

3. Option

3.1 介绍

在标准库(std)中有个叫做 Option<T>的枚举类型,用于处理含有 None出现的可能性 的情况。它表现为以下两个option中的一个:

  • Some(T):找到一个属于 T 类型的元素
  • None:找不到相应元素

这些option可以通过 match 显式地处理,或使用 unwrap 隐式地处理。隐式处理如果遇到None就 panic,否则返回 Some 内部的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn give_commoner(gift: Option<&str>) {
match gift {
Some("snake") => println!("Yuck! I'm throwing that snake in a fire."),
// 存在但不是指定的,inner表示变量名
Some(inner) => println!("{}? How nice.", inner),
None => println!("No gift? Oh well."),
}
}

fn give_princess(gift: Option<&str>) {
// `unwrap` 在接收到 `None` 时将返回 `panic`。
let inside = gift.unwrap();
if inside == "snake" { panic!("AAAaaaaa!!!!"); }
println!("I love {}s!!!!!", inside);
}

fn main() {

let bird = Some("robin");
let nothing = None;

give_princess(bird);
give_princess(nothing);
}

3.2 解构Option

Option可以通过?来解构,解构可以获得底层的值,但是?运算符必须在返回Option类型的函数中使用,并且如果要解构的对象是None,则函数直接返回None。例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Person {
job: Option<Job>,
}

#[derive(Clone, Copy)]
struct Job {
phone_number: Option<PhoneNumber>,
}

#[derive(Clone, Copy)]
struct PhoneNumber {
area_code: Option<u8>,
number: u32,
}

impl Person {
fn work_phone_area_code(&self) -> Option<u8> {
// 没有`?`运算符的话,这将需要很多的嵌套的 `match` 语句来解构,如main函数中所示。
self.job?.phone_number?.area_code
}
}

在main函数中使用?运算符解构,必须嵌套一层返回Option的函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn test_opt() -> Option<u8> {
let p = Person {
job: Some(Job {
phone_number: Some(PhoneNumber {
area_code: Some(123),
number: 439222222,
}),
}),
};

let opt_val = p.work_phone_area_code();
// 如果这里opt_val为None,后面的print不会执行,直接返回None
let val = opt_val?;

println!("Here {}", val);

return opt_val
}

fn main() {
test_opt();
}

常规match解构,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let p = Person {
job: Some(Job {
phone_number: Some(PhoneNumber {
area_code: Some(5),
number: 439222222,
}),
}),
};

let val = p.work_phone_area_code();
match val {
Some(5) => println!("val is right"),
Some(v) => println!("val is {}", v),
None => println!("empty.")
}
}

4 Result

Result是Option更丰富的版本,描述的是可能的错误,而不是可能的不存在。

也就是说,Result<T,E> 可以有两个结果的其中一个:

  • Ok<T>:找到 T 元素
  • Err<E>:找到 E 元素,E 即表示错误的类型。

按照约定,预期结果是 “Ok”,而意外结果是 “Err”。

Result也有unwrap()方法,如果存在错误,就panic,否则返回元素T。

Result还有个expect方法,用于在panic前打印信息。

1
2
3
4
5
6
7
8
9
10
11
12
fn parse_i32_from_string(number_str: &str) -> i32 {
let num = number_str.parse::<i32>().unwrap();
num
}

fn main() {
// 此时会panic
let num = parse_i32_from_string("dvdsf");
// 此时会返回数字
let num = parse_i32_from_string("123");
println!("{}", num);
}

4.1 向上抛的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::num::ParseIntError;

// 错误向上抛,而不是调用unwrap()进行panic
fn parse_i32_from_string(number_str: &str) -> Result<i32, ParseIntError> {
match number_str.parse::<i32>() {
Ok(num) => Ok(num),
Err(e) => Err(e),
}
}

fn print(result: Result<i32, ParseIntError>) {
match result {
Ok(n) => println!("n is {}", n),
Err(e) => println!("Error: {}", e),
}
}

fn main() {
// 这种情形下仍然会给出正确的答案。
let num = parse_i32_from_string("123");
print(num);
}

4.2 为Result创建别名

1
2
3
type AliasedResult<T> = Result<T, ParseIntError>;

fn multiply(first_number_str: &str, second_number_str: &str) -> AliasedResult<i32> {

4.3 遇到错误提前返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::num::ParseIntError;

fn parse_i32_from_string(number_str: &str) -> Result<i32, ParseIntError> {
match number_str.parse::<i32>() {
Ok(num) => Ok(num),
Err(e) => return Err(e),
}
}

fn print(result: Result<i32, ParseIntError>) {
match result {
Ok(n) => println!("n is {}", n),
Err(e) => println!("Error: {}", e),
}
}

fn main() {
// 这种情形下仍然会给出正确的答案。
let num = parse_i32_from_string("123");
print(num);
}

4.4 获取值而不panic的简便写法

1
2
3
4
5
6
7
fn parse_i32_from_string(number_str: &str) -> Result<i32, ParseIntError> {
// 使用?得到值,如果?位置发生错误会立刻返回。
let num = number_str.parse::<i32>()?;
// 以前的写法,现在已经弃用
let num = try!(number_str.parse::<i32>());
Ok(num)
}

5. 多种错误类型处理

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn double_first(vec: Vec<&str>) -> i32 {
let first = vec.first().unwrap(); // 生成错误 1
2 * first.parse::<i32>().unwrap() // 生成错误 2
}

fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];

println!("The first doubled is {}", double_first(numbers));

println!("The first doubled is {}", double_first(empty));
// 错误1:输入 vector 为空

println!("The first doubled is {}", double_first(strings));
// 错误2:此元素不能解析成数字
}

最常见的做法是将Option与Result互相包裹来处理:

1
2
3
4
5
6
7
8
9
10
11
12
fn fouble_first(vec: Vec<&str>) -> Result<Option<i32>, ParseIntError> {
let opt = match vec.first() {
Some(v) => {
match v.parse::<i32>() {
Ok(n) => Some(Ok(n * 2)),
Err(e) => Some(Err(e))
}
},
None => None,
};
opt.map_or(Ok(None), |r| r.map(Some))
}

6. anyhow使用

dtolnay/anyhow: Flexible concrete Error type built on std: :Error (github.com)

1
2
3
4
5
6
7
8
use anyhow::{Result, anyhow};

fn test_fun(val: u64) -> Result<u64> {
if val == 0 {
return Err(anyhow!("args is 0."));
}
Ok(128)
}

Rust系统编程之错误处理
http://helloymf.github.io/2023/04/10/rust-xi-tong-bian-cheng-zhi-cuo-wu-chu-li/
作者
JNZ
许可协议