跳到主要内容

Rust错误处理

提示
  1. Rust错误类型:Rust区分不可恢复错误和可恢复错误。不可恢复错误使用 panic! 宏触发,会停止程序执行;可恢复错误通常用 ResultOption 枚举处理,允许程序响应错误。
  2. 不可恢复错误的处理:不可恢复错误(panic)可由数组索引越界等操作触发,也可显式通过 panic! 宏调用。这种错误通常表示严重问题,需立即终止程序。
  3. 可恢复错误的处理:可恢复错误使用 Result<T, E> 枚举处理,其中 Ok(T) 表示成功,而 Err(E) 表示失败。Option<T> 枚举用于可能没有值的情况,Some(T) 表示有值,None 表示无值。

错误是程序中的意外行为或事件,会产生不想要的输出。

在 Rust 中,错误分为两类:

  • 不可恢复的错误
  • 可恢复的错误

Rust 中的不可恢复错误

不可恢复的错误是导致程序停止执行的错误。顾名思义,我们无法从不可恢复的错误中恢复。

这些错误被称为 panic,可以通过调用 panic! 宏显式触发。

让我们看一个使用 panic! 宏的例子。

示例 1:Rust 中带有 panic! 宏的不可恢复错误

fn main() {
println!("Hello, World!");

// 显式退出程序并触发一个不可恢复错误
panic!("Crash");
}

输出

Hello, World!
线程 'main' 在 'Crash', src/main.rs:5:5 处惊慌失措

这里,调用 panic! 宏导致了一个不可恢复的错误。

线程 'main' 在 'Crash', src/main.rs:5:5 处惊慌失措

注意程序仍然执行了 panic! 宏以上的表达式。我们仍然可以在错误消息之前看到屏幕上打印出的 Hello, World!

panic! 宏接受一个错误消息作为参数。

示例 2:Rust 中的不可恢复错误

不可恢复的错误也会在我们执行可能导致代码panic的操作时触发。例如,访问数组超出其索引将导致panic。

fn main() {
let numbers = [1, 2 ,3];

println!("未知索引值 = {}", numbers[3]);
}

错误

error: 这个操作将在运行时panic
--> src/main.rs:4:42
|
4 | println!("未知索引值 = {}", numbers[3]);
| ^^^^^^^^^^ 索引越界:长度为 3 但索引为 3
|

这里,Rust 阻止我们编译程序,因为它知道该操作将在运行时panic。

数组 numbers 在索引 3 处没有值,即 numbers[3]

可恢复错误

可恢复错误是不会停止程序执行的错误。大多数错误是可恢复的,我们可以根据错误类型采取相应的行动。

例如,如果您尝试打开一个不存在的文件,您可以创建该文件,而不是停止程序执行或使用panic退出程序。

让我们看一个例子。

use std::fs::File;

fn main() {
let data_result = File::open("data.txt");

// 使用 match 对 Result 类型进行处理
let data_file = match data_result {
Ok(file) => file,
Err(error) => panic!("打开数据文件时出现问题: {:?}", error),
};

println!("数据文件", data_file);
}

如果 data.txt 文件存在,输出为:

数据文件: File { fd: 3, path: "/playground/data.txt", read: true, write: false }

如果 data.txt 文件不存在,输出为:

线程 'main' 在 '打开数据文件时出现问题: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:8:23 处惊慌失措

Result 枚举

在上述示例中,File::open('data.txt') 的返回类型是 Result<T, E>

Result<T, E> 类型在 Rust 中返回一个值或错误。它是一个 enum 类型,有两种可能的变体。

  • Ok(T) → 操作成功,带有值 T
  • Err(E) → 操作失败,带有错误 E

这里,TE 是泛型类型。要了解更多关于

泛型或泛型类型,请访问 Rust 泛型

判断 Result 枚举是值还是错误的最基本方法是使用 match 表达式进行模式匹配。


// data_file 是一个 Result<T, E> 类型
match data_result {
Ok(file) => file,
Err(error) => panic!("打开数据文件时出现问题: {:?}", error),
};

当结果是 `Ok` 时,这段代码将返回 `file`;当结果是 `Err` 时,它会触发 `panic!`。

要了解更多关于模式匹配,请访问 [Rust 模式匹配](/tutorials/rust/pattern-matching)

## Option 枚举

`Option` 类型或 `Option<T>` 类型是一个枚举类型,就像 `Result` 一样,它有两种可能的变体。

- `None` → 表示没有值的失败情况
- `Some(T)` → 类型为 `T` 的值

我们来看一个例子,

```rust exec
fn main() {
let text = "Hello, World!";

let character_option = text.chars().nth(15);

// 使用 match 表达式处理 Option 类型
let character = match character_option {
None => "empty".to_string(),
Some(c) => c.to_string()
};

println!("索引 15 处的字符是 {}", character);
}

输出

索引 15 处的字符是 empty

这里,方法 text.chars().nth(15) 返回一个 Option<String>。因此,为了从 Option 中获取值,我们使用了 match 表达式。

在上面的例子中,字符串 text 的第 15 个索引不存在。因此,Option 类型返回了一个 None,它匹配到了字符串 "empty"

None => "empty".to_string()

如果我们要获取字符串 text 的第 11 个索引,Option 枚举将会返回 Some(c),其中 c 是第 11 个索引处的字符。

让我们更新上面的例子,来查找字符串中的第 11 个索引。

fn main() {
let text = "Hello, World!";

let character_option = text.chars().nth(11);

// 使用 match 表达式处理 Option 类型
let character = match character_option {
None => "empty".to_string(),
Some(c) => c.to_string()
};

println!("索引 11 处的字符是 {}", character);
}

输出

索引 11 处的字符是 d

Rust 中 Result 和 Option 枚举的区别

Option 枚举可以返回 None,表示操作失败。

然而,有时候表达操作失败的原因是非常重要的。因此,我们有了 Result 枚举,它提供了 Err 以及操作失败的原因。

简而言之,

  • Option 是关于 SomeNone(有值或无值)
  • Result 是关于 OkErr(结果或错误结果)