展开

原文跟踪unwinding.md   Commit: 0e6c680ebd72f1860e46b2bd40e2a387ad8084ad

Rust有一个分层错误处理方案:

  • 如果可能合理地缺少某些东西,则使用Option
  • 如果出现问题并且可以合理地处理,则使用Result
  • 如果出现问题且无法合理处理,则线程会发生panics
  • 如果发生灾难性事件,程序就会中止(abort)。

在大多数情况下,OptionResult绝对是首选,因为它们可以被API用户自行决定提升为恐慌(panics)或中止(abort)。恐慌导致线程停止正常执行并解除其堆栈,调用析构函数就像每个函数都立即返回。

从1.0开始,Rust在恐慌方面有两种想法。在很久很久以前,Rust更像是Erlang。像Erlang一样,Rust有轻量级的任务,当他们到达时,任务的目的是恐慌自杀站不住脚的状态。与Java或C ++中的异常不同,恐慌不可能随时都能被捕获。恐慌只能由任务的所有者捕获,在那里他们必须处理或任务本身panic

展开(unwinding)对于这个故事很重要,因为如果有任务的话没有调用析构函数,它会导致内存和其他系统资源泄漏。由于预期任务会在正常执行期间死亡,因此可以实现对于长时间运行的系统,Rust非常差!

正如我们今天所知道的Rust那样,这种在追求越来越少的抽象方面编程风格不再流行。轻量级的任务被以重量级OS线程杀死。仍然,在1.0的稳定Rust恐慌只能由父线程捕获。这意味着恐慌需要启动整个OS线程!不幸的是,这与有关Rust的零成本抽象哲学冲突。

有一个名为catch_panic的不稳定的API可以引起恐慌没有产生一个线程。尽管如此,我们还是鼓励你在这里谨慎地做。 特别是,Rust的当前展开实现针对"doesn't unwind" 的情况进行了大量优化。如果程序没有展开,那么对于准备好展开的程序不应该有运行时成本。结果,实际上展开将更昂贵。 Java在正常情况下,不构建您的程序来展开。理想情况下,你应该只对编程错误或极端问题panic

Rust的展开策略并未从根本上与任何其他语言的展开兼容。因此,从另一个语言展开Rust,或从Rust展开另一种语言是Undefined Behavior。你必须绝对捕获FFI边界的任何panics!做了什么这取决于你,但必须做这些。如果你没有这样做,充其量,你的应用程序将崩溃。在最坏的情况下,您的应用不会崩溃,但会继续完全破坏的状态。