Skip to content

CONCEPT Cited by 1 source

Unwind safety

Definition

Unwind safety in Rust is the discipline that guarantees code's data structures remain in a valid state even if a panic unwinds through them. The standard library encodes it as the UnwindSafe marker trait: types that can be safely observed after a panic (e.g. types with no shared mutable state, types whose invariants are maintained despite partial operations) implement it; types that cannot do not.

std::panic::catch_unwind(F) requires F: UnwindSafe to prevent callers from observing broken invariants after a caught panic. When the compiler refuses to accept a non-unwind-safe closure, the "escape hatch" is std::panic::AssertUnwindSafe(closure) — a newtype wrapper that lies to the compiler, promising the closure's captured state is safe to observe post-panic.

The footgun

AssertUnwindSafe is the intended last resort, not the default. But when closures capturing shared references are everywhere (as in wasm-bindgen-generated code), the compiler's rejection tends to route developers toward AssertUnwindSafe reflexively, silently lying about invariants that may actually be broken. The result is code that compiles but whose post-panic state is undefined.

Cloudflare's wasm-bindgen mitigation

In moving concepts/panic-unwind support into wasm-bindgen, Cloudflare hit this directly: "This quickly exposed a problem, though: many closures capture references that remain after an unwind, making them inherently unwind-unsafe." Two pieces were added:

  • MaybeUnwindSafe trait — checks UnwindSafe only when built with panic=unwind; under panic=abort the check is vacuous because no unwind can happen. Keeps the common panic=abort build free of false positives while enabling the discipline when it matters.
  • Closure::new_aborting variants — when unwind safety of the captured state can't be guaranteed, these variants terminate on panic rather than unwind. The code "terminates on panic instead of unwinding in cases where unwind safety can't be guaranteed" — preserving correctness by falling back to abort recovery rather than producing a caught panic that leaves broken invariants in memory.

"To avoid a situation where users are encouraged to incorrectly wrap closures in AssertUnwindSafe just to satisfy the compiler, we added Closure::new_aborting variants."

The design trades one-request-state-preservation (the benefit of panic=unwind) for correctness when safety can't be proven — which is structurally the right call.

Seen in

Last updated · 510 distilled / 1,221 read