Drop / RAII
Nitpick's Drop mechanism is the opt-in RAII layer that
auto-cleans-up resources at scope exit. Importing
stdlib/drop.npk
enables drop emission for a fixed set of resource regions —
wild struct bindings, wildx pointer bindings, HandleArena
handles, and JitFn pages — without changing the source-level
spelling of those regions.
Without the import, those bindings keep the v0.29.2-and-earlier
explicit-cleanup contract: you call npk_free / wildx_free /
HandleArena.destroy / Jit.free yourself, and ARIA-014 fires
on leaks. Drop is purely additive.
Quick reference
With use "drop.npk".*; |
Auto-emit at scope end |
|---|---|
wild T:x = T{ ... }; |
npk_free(x) |
wildx T->:p = wildx_alloc(N); |
npk_wildx_free(p) |
int64:a = HandleArena.create(); |
npk_handle_arena_destroy(a) |
wildx int8->:f = Jit.compile_add_i32(); |
npk_wildx_free(f) |
Each region has its own opt-in sentinel struct
(NitpickWildRaii, NitpickWildxRaii,
NitpickHandleArenaRaii, NitpickJitFnRaii). The compiler keys
on the presence of each impl:Drop:for:NitpickXxxRaii
declaration. They all currently flip on the same drop.npk
import; a future cycle may let a program enable one without the
others.
When Drop runs
Drop calls fire at every scope-exit path:
- Fall-through — the block reaches its closing
}without a terminator. pass v— early successful exit from a function.fail e— early TBB-error exit.return Result{...}— early return inside a fallible function.
Order within a scope is reverse declaration order
(DROP-DEC-003). Across nested scopes, drops walk
innermost-out. Drops always run before defer blocks
(DROP-DEC-010).
When Drop does not run
exit Nis a hard process exit. Drops are skipped (DROP-DEC-008). Same semantics as C++_Exitor Rustprocess::exit. Reach forpass/failif you need destructors to run.pass v/return vwherevis a bare identifier — the binding named byvhas its drop skipped; ownership moves to the caller (DROP-DEC-004 move semantics, bare- identifier only this cycle). Every other binding in scope still drops.- Bindings of unsupported shapes —
wild T->:p = alloc(...)(raw pointer-typed wild) and primitive bindings keep the explicit-cleanup contract. Per-Handle<T>auto-free is now supported as of v0.31.5.x —Handle<T>:h = HandleArena.alloc(a, N);auto-emitsnpk_handle_free(h)at scope end whendrop.npkis imported. See regions.md.
Comparison: Drop vs failsafe vs explicit free
| Mechanism | Runs on | Order | Opt-in |
|---|---|---|---|
| Drop | fall-through, pass, fail, return |
reverse decl | use "drop.npk".*; |
defer |
same as Drop | reverse defer |
always available |
failsafe |
fail propagation in main |
top of main body |
always available |
| Explicit free | wherever you write it | source order | always available |
Drop and defer are not mutually exclusive — a function may
have both. Drops run first, defers run after. Inside a
single scope, Drop touches only the four supported region
shapes; defer runs arbitrary user code.
Chapters
- README — this page.
- Surface —
impl:Drop:for:T, thedropmethod shape, what's allowed and what isn't. - Regions — Drop for
wild/wildx/Handle/Arena/JitFn. The RAII-vs-explicit decision table. - Ordering — block reverse order, struct-field
reverse order,
pass/failsemantics,failsafeinteraction, drop-before-defer. - Pitfalls — moves out of bindings (no drop),
destructor failure, drop-during-drop, hard
exit, theJit_compile_*forcing-function gotcha. - NLL —
#[nll_drop]function attribute, last-use early-drop,#[lexical_drop]per-binding override, ARIA-052, borrow-lifetime tightening (v0.31.7.x). - FAQ — RAII vs manual, NLL Q&A, why no Drop for
primitives, can
dropallocate? can itpass?
See also
stdlib/drop.npk— the opt-in source.guide/memory/wild.md— explicitwild/wildxlifetime model that Drop layers on top of.guide/handles/—HandleArenalifetimes; Drop wrapsHandleArena.create()with auto-destroy.guide/jit/— JIT pages; Drop wrapsJit.compile_add_i32()with auto-free.
Validation snapshot (v0.29.7)
- CTest: 85/85 (84 at v0.29.6 close; +1 =
bug_tests_v0297). - K core: 151/151. K proofs: 11/11.
- Bug regressions:
bug279–bug310covering surface, per-region codegen, early-exit drops, move semantics, and the before-defer ordering. - Decisions locked: DROP-DEC-003 (reverse declaration order),
DROP-DEC-004 (bare-identifier move), DROP-DEC-007 (per-region
opt-in flags), DROP-DEC-008 (
exitskips drops), DROP-DEC-010 (drops before defers).