展开(Unwinding)

Rust有一个 分层的(tiered) 错误处理方案:

  • 如果可能合理地缺少某些东西,则使用Option.

  • 如果出现问题并且可以合理地处理,则使用Result.

  • 如果出现问题且无法合理处理,则线程会发生恐慌(panics).

  • 如果发生灾难性事件,程序就会中止.

在大多数情况下,Option和Result绝对是首选,特别是因为它们可以在API用户的判断下被提升为恐慌或中止.Panics导致线程停止正常执行并展开其堆栈,调用析构函数就像每个函数立即返回一样.

从1.0开始,Rust在恐慌方面有两种想法.在很久很久以前,Rust更像是Erlang.像Erlang一样,Rust拥有轻量级的任务(tasks),当它们达到一种无法维持的状态时,任务的目的就是用恐慌杀死它们.与Java或C++中的异常不同,恐慌无法被随时捕获.恐慌只能由任务的所有者捕获,此时它们必须被处理或 该(that) 任务本身会恐慌.

展开对于这个故事很重要,因为如果没有调用任务的析构函数,它将导致内存和其他系统资源泄漏.由于预期任务会在正常执行期间死亡,这会使Rust在长时间运行的系统中非常糟糕!

正如我们今天所知道的Rust那样,这种编程风格在追求越来越少的抽象时已经过时了.轻量级任务以重量级OS线程的名义被杀死.仍然,在1.0版本的稳定Rust中,恐慌只能由父线程捕获.这意味着捕获恐慌需要启动整个OS线程!不幸的是,这与Rust的零成本抽象哲学相冲突.

有一个名为catch_unwind的API可以在不产生线程的情况下捕获恐慌.尽管如此,我们还是鼓励你们谨慎地做到这一点.特别是,Rust的当前展开实现针对”不展开(doesn’t unwind)”的情况进行了大量优化.如果程序没有展开,那么对于 准备好(ready) 展开的程序应该没有运行时成本.因此,实际上展开将比Java中类似的更昂贵.在正常情况下,不要构建你的程序以展开.理想情况下,你应该只对编程错误或 极端(extreme) 问题恐慌.

Rust的展开策略并未指定与任何其他语言的展开基本兼容.因此,从另一种语言展开到Rust,或从Rust中展开另一种语言是Undefined Behavior.你必须 绝对(absolutel) 捕获FFI边界的任何恐慌!你在那时所做的事取决于你,但必须采取 一些措施(something) .如果你没有这样做,最好情况,你的应用程序将崩溃并烧毁.在最坏的情况下,你的应用程序 不会(won’t) 崩溃和烧毁,将继续完全破坏状态.