Panics in unsafe code¶
Level: 301 · deep dive
One line: A panic can leave any function halfway through, and in safe code that costs a wrong answer; halfway through unsafe code it can leave an invariant broken that a destructor then relies on — set_len(n) before the n elements exist becomes undefined behaviour the moment the code filling them panics.
Stub — an outline, not a lesson. There is no runnable example behind this page yet, so nothing on it has been through the check that backs every other claim in this library. The bullets below are the questions the finished page has to answer.
What it has to cover¶
- The window: every call that can panic between the line that breaks an invariant and the line that restores it — a
clone(), a user closure, anOrd::cmp, an index out of bounds, an arithmetic overflow in a debug build. How do you list them for oneunsafeblock? - The worked failure:
Vec::with_capacity(n), thenset_len(n), then elements written throughas_mut_ptr()by a closure that panics on the second one. Unwinding drops theVec, which dropsnelements of which one exists. Under Miri this is "reading memory … but memory is uninitialized"; what does the native build do? - The first fix is ordering: write the elements, then
set_len. What doesspare_capacity_mutdo to make that order the natural one? - The second fix is a guard: a local struct whose
Droprestores the invariant if unwinding reaches it. std's ownalloccode hasSetLenOnDropinVecandHoleinBinaryHeap— what does each restore, and why does theDroprun on the panic path and the normal path alike?std::mem::DropGuardwould name the pattern, and in 1.98 it is nightly-only (drop_guard, issue #144426). UnwindSafe↗ is an auto trait but not anunsafeone; its docs describe it as a speed bump rather than a contract, andAssertUnwindSaferemoves it with nounsafeat all. What does it stop, and what does it leave entirely to you?panic = "abort"removes unwinding, and with it this whole page — for a binary. Why can a library never assume its caller chose it?- A panic that reaches an
extern "C"boundary aborts; the unsafe-code consequences of that belong to Callbacks across FFI.
The trap it exists for¶
The fast path written as set_len plus raw writes, and tested only with closures that never panic. Every test passes, and the first panicking input — a Clone impl that indexes past an end, a caller's closure that unwraps — turns a recoverable panic into undefined behaviour inside Drop.
Where this sits¶
- What a panic costs covers unwinding in safe code, where memory stays tidy and work stays half-done; Lock poisoning covers how std records a broken invariant for a lock;
Vec::set_lencovers that method's two conditions. This page covers only exception safety inside unsafe code.
See also¶
- What a panic costs — unwinding,
catch_unwindandpanic = "abort" Vec::set_len— "initialises nothing, drops nothing, checks nothing"- Lock poisoning — the safe-code version: record that the invariant may be broken
Drop, and what RAII buys — the mechanism a guard is built from- What an invariant is — the property the window leaves broken
- Miri — how the failure above was observed
- Callbacks across FFI — where a panic meets a C frame
If you are coming from another language¶
- C++. This page is C++'s exception-safety guarantees — basic, strong, no-throw — applied to
unsafeblocks.std::vector::push_backoffers the strong guarantee and relies onnoexceptmove constructors to do it; the guard struct here is the RAII "rollback on unwind" idiom C++ programmers already write. What Rust changes is the default: safe code needs none of this, so the discipline only applies whereunsafeis written. - C. No unwinding, so no panic mid-block — but
longjmpout of a half-updated structure is the same hazard, and skipping a Rust frame's destructors withlongjmpbreaks an assumption the Reference lists under undefined behaviour. - Java and Python. An exception thrown between two field writes leaves an object in a state its methods do not expect, and
finallyis the repair. The consequence stops at wrong answers, because neither runtime lets an object's memory be read as a type it does not hold — which is the partunsafeRust gives up.
Po polsku¶
Panika (panic) może przerwać każdą funkcję w połowie. W bezpiecznym kodzie kosztuje to złą odpowiedź, ale w środku bloku unsafe może zostawić złamany niezmiennik, na którym polega potem destruktor (Drop) — set_len(n) wywołane, zanim n elementów istnieje, staje się niezdefiniowanym zachowaniem, gdy tylko kod wypełniający bufor spanikuje. Są dwa lekarstwa: kolejność (najpierw zapis, potem set_len) i strażnik (guard) — lokalna struktura, której Drop przywraca niezmiennik podczas odwijania stosu. UnwindSafe to tylko „próg zwalniający”, nie kontrakt.
Szukaj po polsku: bezpieczeństwo wyjątków · odwijanie stosu · strażnik RAII · rust exception safety unsafe · rust set_len panic safety · nomicon exception safety