mirror of
https://github.com/RustyCab/LearnRustEasy.git
synced 2026-02-03 18:23:31 +08:00
Update chapter_3_12.md
This commit is contained in:
@@ -1,3 +1,171 @@
|
||||
# 错误处理
|
||||
# 3.12 错误处理
|
||||
Rust将错误分为两大类:可恢复的和不可恢复的错误。
|
||||
- 可恢复错误通常代表向用户报告错误和重试操作是合理的情况,例如未找到文件。rust中使用```Result```来处理可恢复错误。
|
||||
- 不可恢复错误是bug的同义词,如尝试访问超过数组结尾的位置。rust中通过```panic!```来实现。
|
||||
|
||||
todo
|
||||
## 3.12.1 用panic!处理不可恢复错误
|
||||
panic的使用方式如下:
|
||||
```Rust
|
||||
fn main() {
|
||||
panic!("crash and burn");
|
||||
}
|
||||
```
|
||||
运行该程序会打印如下错误:
|
||||

|
||||
运行时添加```RUST_BACKTRACE=1```,可以打印完整的堆栈,上面的代码运行时加```RUST_BACKTRACE=1```的结果如下:
|
||||

|
||||
|
||||
## 3.12.2 用Result处理可恢复错误
|
||||
### 1. Result的定义
|
||||
Rust中使用```Result```类型处理可恢复错误,其定义如下:
|
||||
```Rust
|
||||
enum Result<T, E> {
|
||||
Ok(T),
|
||||
Err(E),
|
||||
}
|
||||
```
|
||||
```T```和```E```是泛型类型参数,```T```代表成功是返回的```Ok```成员中的数据类型,```E```代表失败是返回的```Err```成员中的错误的类型。
|
||||
|
||||
### 2. 使用Result
|
||||
使用示例如下:
|
||||
```Rust
|
||||
use std::fs::File;
|
||||
fn main() {
|
||||
let f = File::open("hello.txt");
|
||||
let _r = match f {
|
||||
Ok(file) => {
|
||||
file
|
||||
},
|
||||
Err(error) => {
|
||||
panic!("Problem opening the file: {:?}", error)
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
第3行返回的结果就是一个```Result```类型,可以使用```match```匹配```Result```的具体类型。下面为使用```str```作为```Result<T, E>```中的错误```E```的例子:
|
||||
```Rust
|
||||
// 该函数返回结果为Result<T, E>,其中T为(),E为具有静态生命周期的&str类型
|
||||
fn produce_error(switch: bool) -> Result<(), &'static str> {
|
||||
if switch {
|
||||
return Err("This is a error");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn main() {
|
||||
let result = produce_error(true);
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
println!("There is no error!");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error is: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 失败时直接panic的简写
|
||||
对于返回```Result```类型的函数,不用总是使用```match```去匹配结果,还可以使用简写获取到```Result<T, E>```中的```T```类型,不过使用简写时,当```Result<T, E>```是```Err```时程序会panic:
|
||||
|
||||
- 使用unwrap简写:
|
||||
```Rust
|
||||
use std::fs::File;
|
||||
fn main() {
|
||||
let f = File::open("hello.txt").unwrap(); //使用unwrap简写来获取到Result中的T类型,
|
||||
//当hello.txt打开失败时程序会panic
|
||||
}
|
||||
```
|
||||
|
||||
- 使用except简写:
|
||||
```Rust
|
||||
use std::fs::File;
|
||||
|
||||
fn main() {
|
||||
//使用unwrap简写来获取到Result中的T类型,
|
||||
//当hello.txt打开失败时程序会panic
|
||||
let greeting_file = File::open("hello.txt")
|
||||
.expect("hello.txt should be included in this project");
|
||||
}
|
||||
```
|
||||
### 4. 传播错误
|
||||
除了函数中处理错误外,还可以选择让调用者知道这个错误并决定如何处理,这叫做传播错误。示例如下:
|
||||
```Rust
|
||||
fn produce_error(switch: bool) -> Result<(), &'static str> {
|
||||
if switch {
|
||||
return Err("This is a error");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transmit_error(flag: bool) -> Result<String, &'static str> {
|
||||
let s = produce_error(flag);
|
||||
match s {
|
||||
Ok(_) => return Ok("Hello".to_string()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let result = transmit_error(true);
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
println!("There is no error!");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error is: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
在上面的代码中,```transmit_error```中就没有自己处理错误,而是选择将错误传递给外层。
|
||||
|
||||
传播错误可以用```?```进行简写,上面的```transmit_error```函数代码用简写方式示例如下:
|
||||
```Rust
|
||||
fn produce_error(switch: bool) -> Result<(), &'static str> {
|
||||
if switch {
|
||||
return Err("This is a error");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transmit_error(flag: bool) -> Result<String, &'static str> {
|
||||
produce_error(flag)?; // 如果调用produce_error函数返回的是Err类型将会直接从此行返回
|
||||
|
||||
println!("如果produce_error函数返回的是错误将不会执行到这里");
|
||||
Ok("Hello".to_string())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// 下面的调用将不会打印"如果produce_error函数返回的是错误将不会执行到这里"
|
||||
let result1 = transmit_error(true);
|
||||
|
||||
println!("+++++++++++++++++++++++++++++++++++++++++++++++");
|
||||
// 下面的调用将会打印"如果produce_error函数返回的是错误将不会执行到这里"
|
||||
let result2 = transmit_error(false);
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
下面是更复杂的简写:
|
||||
```Rust
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::fs::File;
|
||||
fn read_username_from_file() -> Result {
|
||||
let mut s = String::new();
|
||||
//如果open()失败将直接把“打开失败错误”返回,如果read_to_string失败也将把“读取失败错误”返回
|
||||
File::open("hello.txt")?.read_to_string(&mut s)?;
|
||||
Ok(s)
|
||||
}
|
||||
```
|
||||
```?```运算符被用于返回```Result```的函数,```Result```返回的是```Err```类型,则直接结束将错误传播到上一级。
|
||||
|
||||
## 3.12.3 什么时候使用panic
|
||||
关于什么时候使用panic,什么使用Result,总结如下:
|
||||
|
||||
- 示例、代码原型和测试适合使用panic,使用Result可以使用unwrap、expect的方式;
|
||||
- 实际项目中应该多使用Result,并且尽量少使用Result的unwrap、expect方式。
|
||||
|
||||
Reference in New Issue
Block a user