Handles
A handle is an opaque 64-bit token (Handle<T>) that names an
allocation inside a generational arena. Handles give you:
- Region-style cheap allocation — bump into an arena, free the
whole arena at once with
HandleArena.destroy. - Stable identity across moves — handles are pure values; you can copy them, store them in structs, hand them to FFI, without changing what they refer to.
- Generation-checked dereference — once an arena is destroyed (or
a slot is freed), every outstanding handle to it dereferences to
0i64(NULL). No undefined behaviour, no use-after-free. - A compile-time outlives rule — within a function body, derefing
a handle whose arena was destroyed is a static error
(
ARIA-032).
When to reach for a handle vs gc / wild
| You want… | Use |
|---|---|
| Lots of short-lived allocations, all freed together | Handle |
| One thing collected when nothing references it | gc |
Explicit alloc / defer free with a known shape |
wild |
| Executable memory with W^X lifecycle | wildx |
Handles sit between gc (zero ceremony, arbitrary lifetime) and
wild (full manual control). They are the right answer when you have
a bulk of allocations whose lifetime matches a known scope (a
request, a parse, a frame) and you would otherwise be writing your
own free list.
Chapters
- README — this page.
- Arenas —
HandleArena.create/destroy, capacity, when to use one big arena vs many small ones. - Handles —
Handle<T>, allocation, deref, free, the generation counter, runtime UAF behaviour. - Lifetimes — the borrow-checker rule that catches
handle-outlives-arena at compile time (
ARIA-032). - FFI — handing a handle's underlying pointer across the C
boundary without losing the safety net, plus the
#[destroys_arena(<param>)]attribute for externs. - Cross-module — how
ARIA-032flows acrossuseboundaries: imported function summaries, transitive destroy/escape, cross-module$$ireturn-borrow. - Diagnostics — every handle-related error / warning, smallest reproducer, canonical fix.
- FAQ — recurring questions about handles.
See also
guide/drop/— the opt-in RAII layer that auto-emitsnpk_handle_arena_destroy(a)forint64:a = HandleArena.create();bindings (v0.29.5) andnpk_handle_free(h)forHandle<T>:h = HandleArena.alloc(a, N);bindings (v0.31.5.x) at scope end. Importingdrop.npkopts both tiers in; theraw(...)wrapper at a binding's declaration site peels a single binding out.
Validation
- Runtime:
tests/runtime/test_handle_v0277.cpp(CTestruntime_handle_v0277). - Typed surface:
bug256–bug259(CTestbug_tests_v0278). - Outlives rule:
bug260–bug263(CTestbug_tests_v0279).