恐慌的不可恢复的错误!

有时,你的代码中会发生坏事,你无能为力 它。在这些情况下,Rust 具有宏。有两种方法可以导致 Panic 在实践中:通过执行导致代码 panic 的作(例如 访问超过末尾的数组)或通过显式调用宏。 在这两种情况下,我们都会在我们的程序中引起恐慌。默认情况下,这些 panic 将 打印失败消息,展开,清理堆栈,然后退出。通过 环境变量中,您还可以让 Rust 在 panic 的发生是为了更容易追踪 panic 的来源。panic!panic!

展开堆栈或中止以响应 panic

默认情况下,当 panic 发生时,程序开始展开,这意味着 Rust 会回到堆栈中,并清理每个函数中的数据 遇到。然而,走回去清理是一项艰巨的工作。锈 因此,允许您选择立即中止的替代方案, 这将结束程序而不进行清理。

然后,程序正在使用的内存将需要由 操作系统。如果你需要将生成的二进制文件设为 尽可能小,你可以通过以下方式在 panic 时从 unwind 切换到 aborting 添加到 Cargo.toml 文件中的相应部分。例如,如果你想在 release 模式下在 panic 时中止, 添加以下内容:panic = 'abort'[profile]

[profile.release]
panic = 'abort'

让我们尝试在一个简单的程序中调用panic!

文件名: src/main.rs

fn main() {
    panic!("crash and burn");
}

当您运行该程序时,您将看到如下内容:

$ cargo run
   Compiling panic v0.1.0 (file:///projects/panic)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
     Running `target/debug/panic`
thread 'main' panicked at src/main.rs:2:5:
crash and burn
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

调用 to 会导致最后两行中包含的错误消息。 第一行显示了我们的 panic 消息和源代码中的位置,其中 发生 panic: src/main.rs:2:5 表示这是第二行 src/main.rs 文件的第五个字符。panic!

在这种情况下,指示的行是我们代码的一部分,如果我们转到该行 行,我们会看到 macro 调用。在其他情况下,调用可能会 位于代码调用的代码中,以及由 错误消息将是宏所在的其他人的代码 called 调用的 API 代码,而不是最终导致 call 的代码行。panic!panic!panic!panic!

我们可以使用调用来源的函数的回溯来计算 找出导致问题的代码部分。了解如何使用 回溯,让我们看看另一个例子,看看当 由于代码中的 bug 而不是 从我们的代码中直接调用宏。示例 9-1 有一些代码 尝试访问超出有效索引范围的 vector 中的索引。panic!panic!panic!

文件名: src/main.rs

fn main() {
    let v = vec![1, 2, 3];

    v[99];
}

示例 9-1:尝试访问 end 的 Vector 的 end 调用,这将导致对 panic 的调用

在这里,我们尝试访问 vector 的第 100 个元素(位于 索引 99,因为索引从零开始),但向量只有 3 个 元素。在这种情况下,Rust 会 panic。using 应该返回 一个元素,但如果你传递了一个无效的索引,则没有 Rust 可以返回这里,那将是正确的。[]

在 C 语言中,尝试读取数据结构末尾之外的内容是 undefined 行为。您可能会获得内存中位置的任何内容 对应于数据结构中的该元素,即使内存 不属于该结构。这称为缓冲区过度读取,并且可能会 如果攻击者能够纵索引,则会导致安全漏洞 以这样一种方式读取数据,他们不应该被允许读取 数据结构。

为了保护程序免受此类漏洞的影响,如果尝试读取 元素中,Rust 将停止执行并拒绝 继续。让我们试一试,看看:

$ cargo run
   Compiling panic v0.1.0 (file:///projects/panic)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
     Running `target/debug/panic`
thread 'main' panicked at src/main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

此错误指向 main.rs 的第 4 行,我们尝试访问 中向量的索引。99v

该行告诉我们,我们可以设置环境 变量来回溯导致错误的确切原因。回溯是已调用的所有函数的列表,用于实现此 点。Rust 中的回溯与其他语言一样工作:关键 读取回溯是从顶部开始读取,直到您看到 写。这就是问题的根源。该点上方的线条 是您的代码调用的代码;以下几行是将 法典。这些前后行可能包括核心 Rust 代码、标准 库代码或您正在使用的 crate 中。让我们尝试通过 将环境变量设置为除 之外的任何值。 示例 9-2 显示了与你将看到的类似的输出。note:RUST_BACKTRACERUST_BACKTRACE0

$ RUST_BACKTRACE=1 cargo run
thread 'main' panicked at src/main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
stack backtrace:
   0: rust_begin_unwind
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
   2: core::panicking::panic_bounds_check
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:208:5
   3: <usize as core::slice::index::SliceIndex<[T]>>::index
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/slice/index.rs:255:10
   4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/slice/index.rs:18:9
   5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/alloc/src/vec/mod.rs:2770:9
   6: panic::main
             at ./src/main.rs:4:6
   7: core::ops::function::FnOnce::call_once
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

示例 9-2:设置环境变量 RUST_BACKTRACE 时显示的调用 panic! 生成的回溯

这是很大的产出!您看到的确切输出可能会有所不同,具体取决于 在您的作系统和 Rust 版本上。为了使用 this 获取回溯 信息,则必须启用调试符号。调试符号由 default (无论是否使用该标志时), 就像我们在这里一样。cargo buildcargo run--release

在示例 9-2 的输出中,回溯的第 6 行指向 导致问题的项目:src/main.rs 的第 4 行。如果我们不想 我们的计划恐慌,我们应该从指向的位置开始调查 到的第一行提到我们编写的文件。在示例 9-1 中,我们 故意编写了会 panic 的代码,解决 panic 的方法是不要 请求超出 vector 索引范围的元素。当您的代码 panic 的 panic 中,您需要弄清楚代码正在采取什么作 替换为哪些值会导致 panic 以及代码应该做什么。

我们会回到我们应该和不应该使用 处理 “To panic!或者不要惊慌!部分 章。接下来,我们将了解如何使用 从错误中恢复。panic!panic!Result

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准