Learning Rust - 6 错误处理
错误处理
1 Result 处理可恢复的错误
Result
枚举类有以下两个成员:
例1.1:// T, E都是范型类参数
enum Result<T, E> {
Ok(T),
Err(E),
}
以打开文件为例
例1.2: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:?}"),
};
}
File::open
的返回值是Result<T, E>
,泛型参数T
会被File::open
的实现放入成功返回值的类型std::fs::File
,这是一个文件句柄。错误返回值使用的E
的类型是std::io::Error
。
当File::open
成功时,greeting_file_result
变量将会是一个包含文件句柄的Ok
实例。当失败时,greeting_file_result
变量将会是一个包含了更多关于发生了何种错误的信息的Err
实例。
$ cargo run |
匹配不同的错误
例1.3:use std::fs::File;
use std::io::ErrorKind;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {e:?}"),
},
other_error => {
panic!("Problem opening the file: {other_error:?}");
}
},
};
}
2 unwrap 和 expect 等
unwrap()
- 适用于
Option<T>
和Result<T, E>
。 - 直接获取
Some
或Ok
内部的值。 - 如果是
None
或Err
,程序会直接panic
。
例2.1:let x: Result<i32, &str> = Ok(20);
println!("{}", x.unwrap()); // 输出 20
let y: Result<i32, &str> = Err("error");
println!("{}", y.unwrap()); // panic: called `Result::unwrap()` on an `Err` value: "error"
unwrap_err()
- 仅适用于
Result<T, E>
。 - 返回
Err
内部的错误值。 Ok
,程序会直接panic
。
例2.2:let x: Result<i32, &str> = Err("error");
println!("{}", x.unwrap_err()); // 输出 "error"
let y: Result<i32, &str> = Ok(10);
println!("{}", y.unwrap_err()); // panic: called `Result::unwrap_err()` on an `Ok` value: 10
expect(msg)
- 适用于
Option<T>
和Result<T, E>
。 - 作用类似于
unwrap()
,但当panic
时,会输出自定义的错误信息,便于调试。
例2.3:let x: Result<i32, &str> = Ok(20);
println!("{}", x.expect("Result was Err")); // 输出 20
let y: Result<i32, &str> = Err("error");
println!("{}", y.expect("Result was Err")); // panic: Result was Err
expect_err(msg)
- 仅适用于
Result<T, E>
。 - 作用类似于
unwrap_err()
,但panic
时会带上自定义的错误信息。
例2.4:let x: Result<i32, &str> = Err("error");
println!("{}", x.expect_err("Expected an error")); // 输出 "error"
let y: Result<i32, &str> = Ok(10);
println!("{}", y.expect_err("Expected an error")); // panic: Expected an error
总结
方法 | 适用类型 | 作用 | 遇到 None 或 Err 时的行为 |
---|---|---|---|
unwrap() |
Option<T> / Result<T, E> |
获取 Some 或 Ok 内部值 |
panic |
unwrap_or(d) |
Option<T> / Result<T, E> |
获取 Some 或 Ok ,否则返回 d |
返回 d |
unwrap_err() |
Result<T, E> |
获取 Err 内部的错误值 |
panic |
expect(msg) |
Option<T> / Result<T, E> |
获取 Some 或 Ok ,panic 时显示 msg |
panic(带 msg ) |
expect_err(msg) |
Result<T, E> |
获取 Err 内部的错误值,panic 时显示 msg |
panic(带 msg ) |
3 传播错误
当编写一个其实先会调用一些可能会失败的操作的函数时,除了在这个函数中处理错误外,还可以选择让调用者知道这个错误并决定该如何处理。这被称为传播(propagating)错误
,这样能更好的控制代码调用。
例3.1: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),
}
}
传播错误的简写:? 运算符
例3.2: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")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}
如果Result
的值是Ok
,这个表达式将会返回Ok
中的值而程序将继续执行。如果值是Err
,Err
将作为整个函数的返回值,就好像使用了return
关键字一样,这样错误值就被传播给了调用者。
使用链式方法进一步缩短代码:
例3.3: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)?;
Ok(username)
}