Rust 编程语言 - 错误处理

两种类型的错误,可恢复的和不可恢复的。

可恢复的用Result<T,E>处理,不可恢复的使用panic!宏处理

处理不可恢复的错误

  • panic 时,默认会回溯调用栈,并打印调用展;若为了将bin文件变小,可以在配置中设置panic = 'abort', 直接终止
  • panic 可以手动调用,也可能是异常触发的(例如数组访问超索引)
  • 一般会保留调用栈,方便问题排查
  • 通过 RUST_BACKTRACE 环境变量打印 调用栈(必须是debug 模式)

处理可恢复的错误

Result 处理可恢复的错误, Result 是枚举类型

1
2
3
4
enum Result<T, E> {
    Ok(T),
    Err(E),
}

例如,使用match 对Result的处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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,
        // 枚举类型 ErrorKind
        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);
            }
        },
    };

    // 采用闭包的方式处理, 代码会少很多
    let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {:?}", error);
            })
        } else {
            panic!("Problem opening the file: {:?}", error);
        }
    });
}

失败时panic,而不处理

Result 实现的一些方法。

  • unwarp() 若失败,则panic
  • expect("") 可以增加报错提示信息

错误传播

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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),
    }
}

// 简化版本
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)?; // 问号符号
    Ok(username)
}

// 链式之后直接调用
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)
}

必须在返回值为Result 类型的方法中使用?链式调用

是否需要panic

  1. 示例、代码原型和测试都非常适合 panic
  2. 有害状态下适合使用panic,有害状态指:
    1. 当一些假设、保证、协议或不可变性被打破的状态,比如无效值,自相矛盾的值,不存在的值等;
    2. 有害状态是非预期的行为,与偶尔会发生的行为相对,比如用户输入了错误格式的数据
    3. 后续代码依赖的值非预期行为
    4. 没有可行的手段来将有害状态信息编码进所使用的类型中的情况
  3. 当能预期到错误出现时,返回Result更合适
0%