| # Unwinding |
| |
| Rust has a *tiered* error-handling scheme: |
| |
| * If something might reasonably be absent, Option is used. |
| * If something goes wrong and can reasonably be handled, Result is used. |
| * If something goes wrong and cannot reasonably be handled, the thread panics. |
| * If something catastrophic happens, the program aborts. |
| |
| Option and Result are overwhelmingly preferred in most situations, especially |
| since they can be promoted into a panic or abort at the API user's discretion. |
| Panics cause the thread to halt normal execution and unwind its stack, calling |
| destructors as if every function instantly returned. |
| |
| As of 1.0, Rust is of two minds when it comes to panics. In the long-long-ago, |
| Rust was much more like Erlang. Like Erlang, Rust had lightweight tasks, |
| and tasks were intended to kill themselves with a panic when they reached an |
| untenable state. Unlike an exception in Java or C++, a panic could not be |
| caught at any time. Panics could only be caught by the owner of the task, at which |
| point they had to be handled or *that* task would itself panic. |
| |
| Unwinding was important to this story because if a task's |
| destructors weren't called, it would cause memory and other system resources to |
| leak. Since tasks were expected to die during normal execution, this would make |
| Rust very poor for long-running systems! |
| |
| As the Rust we know today came to be, this style of programming grew out of |
| fashion in the push for less-and-less abstraction. Light-weight tasks were |
| killed in the name of heavy-weight OS threads. Still, on stable Rust as of 1.0 |
| panics can only be caught by the parent thread. This means catching a panic |
| requires spinning up an entire OS thread! This unfortunately stands in conflict |
| to Rust's philosophy of zero-cost abstractions. |
| |
| There is an API called [`catch_unwind`] that enables catching a panic |
| without spawning a thread. Still, we would encourage you to only do this |
| sparingly. In particular, Rust's current unwinding implementation is heavily |
| optimized for the "doesn't unwind" case. If a program doesn't unwind, there |
| should be no runtime cost for the program being *ready* to unwind. As a |
| consequence, actually unwinding will be more expensive than in e.g. Java. |
| Don't build your programs to unwind under normal circumstances. Ideally, you |
| should only panic for programming errors or *extreme* problems. |
| |
| Rust's unwinding strategy is not specified to be fundamentally compatible |
| with any other language's unwinding. As such, unwinding into Rust from another |
| language, or unwinding into another language from Rust is Undefined Behavior. |
| You must *absolutely* catch any panics at the FFI boundary! What you do at that |
| point is up to you, but *something* must be done. If you fail to do this, |
| at best, your application will crash and burn. At worst, your application *won't* |
| crash and burn, and will proceed with completely clobbered state. |
| |
| [`catch_unwind`]: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html |