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)
}

