【无标题】

什么是panic

在Rust 中,有一类错误叫作panic。示例如下:

js 复制代码
fn main(){
  let x:Option<i32>=None;
  x.unwrap();
}

编译,没有错误,执行这段程序,输出为:

js 复制代码
thread '<main>'panicked at 'called `Option::unwrap() on a None`value',../
src/libcore/option.rs:326
note:Run with RUST_BACKTRACE=1`for a backtrace.

这种情况就引发了一个panic 。在这段代码中,我们调用了Option::unwrap()方法, 正是这个方法有可能导致panic。根据提示,我们设置一个环境变量RUST_BACKTRACE=1之后再执行这个程序,可以看到这个程序在发生panic 时候的函数调用栈。

执行RUST_BACKTRACE=1 ./test,结果为:

js 复制代码
thread 'main'panicked at 'called `Option::unwrap()`on a None`value',../src/ libcore/option.rs:323
stack backtrace:
1:0x10af488f8-std::sys::backtrace::tracing::imp::write::h6f1d53a70916b90d
2:0x10af4a3af-std::panicking::default_hook::{{closure}}::h137e876f7d3b5850 3:0x10af49945                 -std::panicking::default_hook::h0ac3811ec7cee78c
4:0x10af49e96-std::panicking::rust_panic_with_hook::hc303199e04562edf
5:0x10af49d34-std:: ::begin_panic::h6ed03353807cf54d
6:0x10af49c52-std::panicking::begin_panic_fmt::hc321cece241bb2f5
7:0x10af49bb7-rust_begin_unwind
8:0x10af6f0b0-core::panicking::panic_fmt::h27224b181f9f037f
9:0x10af6efb4-core::panicking::panic::h53676c30b3bd95eb
10:0x10af44804-<core::option::Option<T>>::unwrap::h3478e42c3c27faa3
11:0x10af44880-test::main::h8a7a35fa594c0174
12:0x10af4a96a-rust_maybe_catch_panic
13:0x10af49486-std::rt::lang_start::h538f8960e7644c80
14:0x10af448b9-main

我们去查一下Option::unwrap() 的文档,其中是这么说的:

js 复制代码
Moves the value v out of the Option<T>if it is Some(V).
Panics
Panics if the self value equals None.
Safety note
In general,because this function may panic,its use is discouraged.
Instead,prefer to use pattern matching and handle the None case explicitly. 
  • 当 Option 内部的数据是Some 时,它可以成功地将内部的数据move 出来返回。
  • 当 Option 内部的数据是None 时,它会发生panic 。panic 如果没有被处理,它会导致 整个程序崩溃。

在 Rust 中,正常的错误处理应该尽量使用Result类 型 。Panic 则是作为 一种 "fail fast" 机制,处理那种万不得已的情况。比如,上面例子中的unwrap 方法,试图把Option转换为i32 类型,当参数 是 None 的时候,这个转换是没办法做到的,这种时候就只能使用panic 。所以, 一般情况 下,用户应该使用unwrap_or等不会制造panic 的方法。

在Rust 中 ,Panic 的实现机制有两种方式:unwind 和 abort。

  • unwind 方式在发生panic 的时候,会一层一层地退出函数调用栈,在此过程中,当前 栈内的局部变量还可以正常析构。
  • abort方式在发生panic 的时候,会直接退出整个程序。

在常见的操作系统上,默认情况下,编译器使用的是unwind 方式。所以在发生panic 的时候,我们可以通过一层层地调用栈找到发生panic 的第一现场,就像前面例子展示的 那样。

但 是 ,unwind 并不是在所有平台上都能获得良好支持的。在某些嵌入式系统上, unwind 根本无法实现,或者占用的资源太多。在这种时候,我们可以选择使用abort 方式实现 panic。

编译器提供了一个选项,供用户指定panic 的实现方式。如下所示:

js 复制代码
rustc -C panic=unwind test.rs
rustc -C panic=abort test.rs

可以试试上面两个编译命令,做一下对比。可以看到它们生成的代码,panic 时的行 为是不一样的,生成的可执行程序大小也不同。

Rust 中,通过unwind 方式实现的panic, 其内部的实现方式基本与C++ 的异常是一样 的。而且,Rust 提供了一些工具函数,可以让用户在代码中终止栈展开。示例如下:

js 复制代码
use std::panic;
fn main(){
  panic::catch_unwind(||{
  let x:Option<i32>=None;
  x.unwrap();
  println!("interrupted.");
}).ok();
println!("continue to execute."); }

编译执行可见,在unwrap 语句后面的这条打印语句并未执行。因为在上一条语句中触 发了panic, 这个函数调用栈开始销毁。但是我们有一句catch_unwind阻止了函数调用栈 的继续展开,就像C++ 里面的try-catch 机制一样。因此,main 方法并没有继续被销毁,最 后那条语句可以正常打印输出。

如果我们尝试使用"-c panic=abort" 选项编译上面的代码,可以看到这个catch_unwind 起不了什么作用,最后那条语句无法正常打印输出。

但是,请大家注意,这个catch_unwind 机制绝对不是设计用于模拟"try-catch"机 制的。请大家永远不要利用这个机制来做正常的流程控制。 Rust 推荐的错误处理机制是用 返回值,第33章讲解Rust 的错误处理机制。 panic 出现的场景一般是:如果继续执行下去 就会有极其严重的内存安全问题,这种时候让程序继续执行导致的危害比崩溃更严重,此时 panic 就是最后的一种错误处理机制。它的主要用处参考下面的情况:

  • 在FFI 场景下的时候,当C 语言调用了Rust 的函数,在Rust 内部出现了panic, 如果 这个panic 在 Rust 内部没有处理好,直接扔到C 代码中去,会导致C 语言产生"未定 义行为"(undefined behavior)。
  • 某些高级抽象机制需要阻止栈展开,比如线程池。如果一个线程中出现了panic, 我 们希望只把这个线程关闭,而不至于将整个线程池"拖下水"。

C++ 中引入了"异常"这个机制之后,同时也带入了一个"异常安全"(exception safety) 的概念。对这个概念不熟悉的读者,建议阅读以下文档:

异常安全存在四种层次的保证:

  • No-throw这种层次的安全性保证了所有的异常都在内部正确处理完毕,外部毫无 影 响 ;
  • Strong exception safety------强异常安全保证可以保证异常发生的时候,所有的状态都 可以"回滚"到初始状态,不会导致状态不 一 致的问题;
  • Basic exception safety------基 本 异 常 安 全 保 证 可 以 保 证 异 常 发 生 的 时 候 不 会 导 致 资 源泄 漏 ;
  • No exception safety------没有任何异常安全保证。
相关推荐
微小冷2 小时前
Rust实战教程:做一个UDP聊天软件
rust·udp·egui·聊天软件·rust教程·用户图形界面
小裕哥略帅2 小时前
订单管理--实时算出在途数量、收货数量、到货数量、已发货数量和未发货数量
java·开发语言
_OP_CHEN2 小时前
C++进阶:(五)map系列容器的全面解析
开发语言·c++·map·红黑树·stl容器·键值对·mapoj题
大米粥哥哥2 小时前
c++ libcurl报错Send failed since rewinding of the data stream failed【已解决】
开发语言·c++·http·curl·rewind
Nebula_g2 小时前
C语言应用实例:解方程(二分查找)
c语言·开发语言·学习·算法·二分查找·基础
散峰而望2 小时前
C语言刷题-编程(一)(基础)
c语言·开发语言·编辑器
Dxxyyyy2 小时前
零基础学JAVA--Day27(注释+异常+异常处理方法)
java·开发语言
三品吉他手会点灯3 小时前
stm32f103学习笔记-16-RCC(第2节)-讲解系统时钟配置函数SetSysClockTo72()
笔记·stm32·单片机·嵌入式硬件·学习
视图猿人3 小时前
RxJS基本使用及在next.js中使用的例子
开发语言·javascript