Rust基础——错误处理
开始
rust中的错误处理并不像其他语言一样使用异常处理错误,rust将错误分为不可恢复错误和可恢复错误
- 对于可恢复错误,使用
Result<T, E>
类型进行处理 - 对于不可恢复错误,使用panic进行处理
不可恢复错误
使用panic来处理不可恢复的错误,有两种行为会产生panic
- 代码执行错误,如越界访问
显式调用
panic!
宏1
panic!("Error Message");
产生panic后,panic会打印一个错误信息,之后执行栈展开或终止
- 栈展开:默认行为,rust会回溯每一个栈帧,清理每个栈帧中的数据,最后退出程序
- 终止:rust不清理数据,直接退出程序,数据交由操作系统进行清理
打印错误信息时,设置环境变量RUST_BACKTRACE=1
,可打印出回溯栈
可恢复错误
rust使用Result<T, E>
类型来处理可恢复错误,Result
类型是一个枚举
1
2
3
4
enum Result<T, E> {
Ok(T), // 成功时,包含成功的返回值
Err(E), // 错误时,包含错误对象
}
以打开文件为例,打开文件时会返回一个Result
对象,通过模式匹配进行解构
1
2
3
4
5
6
7
8
9
10
use std::fs::File;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {error:?}"),
};
}
若需要判断错误的类型,通常可以调用
error.kind()
获取一个错误类型的枚举,继续进行模式匹配解构
Err分支简写
在匹配到Err
并且显式调用panic!
宏的情况下,有两个函数可以简化匹配判断
unwrap()
:抛出的panic包含默认错误信息expect()
:抛出的panic包含自定义错误信息
1
2
let file1 = File::open("hello.txt").unwrap();
let file2 = File::open("hello.txt").expect("My message");
传播错误
当函数需要向外部抛出Err
时,函数的返回值应该使用Result<T, E>
类型
以打开文件并读取文件内容为例,打开文件和读取文件都可能发生错误,以函数返回值的形式将错误抛出到外部
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let username_file_result = File::open("hello.txt");
let mut username_file = match username_file_result {
Ok(file) => file,
Err(e) => return Err(e), // 抛出错误
};
let mut username = String::new();
match username_file.read_to_string(&mut username) {
Ok(_) => Ok(username),
Err(e) => Err(e), // 抛出错误
}
}
?
运算符
使用?
运算符简化抛出错误的分支
1
2
3
4
5
6
7
8
9
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("hello.txt")?; // 若发生错误,返回Err
let mut username = String::new();
username_file.read_to_string(&mut username)?; // 若发生错误,返回Err
Ok(username) // 返回成功值
}
进一步简化为链式调用
1
2
3
4
5
6
7
8
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username = String::new();
File::open("hello.txt")?.read_to_string(&mut username)?; // 任何一步发生错误,直接返回Err
Ok(username)
}
?
运算符被定义为提前返回一个“不合法”的值,使用?
运算符需要函数返回值类型与?
运算符的性质兼容
最常用的两种可以使用?
的类型
Result<T, E>
:提前返回Err
值Option<T>
:提前返回None
值
在Option<T>
上使用?
运算符的例子
1
2
3
4
// lines()返回一个迭代器,调用next()获取第一个元素,若迭代器中没有元素,则提前返回None
fn last_char_of_first_line(text: &str) -> Option<char> {
text.lines().next()?.chars().last()
}
rust中提供了一个
Try
trait,对于实现了Try
trait的类型,都可以使用?
运算符简化
本文由作者按照 CC BY 4.0 进行授权