← Return to field notes
systems / systems doctrine

Memory is a lifetime contract

Stack or heap is not a trivia question. It is a promise about ownership, lifetime, movement, and cleanup.

explainer maintained created 2026-06-23 updated 2026-07-18 4 min 3 sections 0 figures
subscribe via RSS report a correction sec intro ~4 min left read 0%
opening contract explainer · maintained
problem
Stack-versus-heap explanations often reduce an ownership and lifetime decision to a simplistic speed comparison.
scope
A systems-level model of lifetime, ownership, movement, sharing, allocation, and cleanup rather than allocator microbenchmarks.
environment
Rust and C++ systems code where value location and ownership affect API and failure behavior.

Assumptions

  • The reader already understands lexical scope and basic allocation.

Limitations

  • Allocator design, escape analysis, arenas, garbage collection, and NUMA behavior need separate treatment.
Table of contents 3 sections
  1. 1 Compare cost shapes, not slogans
  2. 2 Ask which lifetime you are promising
  3. 3 Make the contract visible

Compare cost shapes, not slogans

Stack values are cheap, scoped, and tied to a call frame. Heap values buy flexibility and shared reach, then invoice the system in allocation, synchronization, and ownership complexity.

The relevant cost is not a mythical single stack instruction versus a slow heap call. It includes cache locality, pointer chasing, allocator metadata, fragmentation, page faults, lifetime extension, and synchronization around shared ownership. A heap-backed Vec can still keep its elements contiguous; a large stack frame can still destroy locality or exhaust a thread stack. Placement is only one part of the data-shape decision.

Ask which lifetime you are promising

The useful question is not which one is better, but which lifetime is promised, who may move the value, who may share it, and who is accountable for cleanup.

A borrowed slice says the callee may observe data without owning its storage. Box says the value has one owning handle even though its bytes live on the heap. Arc says lifetime is distributed across owners and cleanup waits for an atomic reference count. Pin adds a location guarantee. Each type communicates a different invalidation and concurrency contract, which is why “put it on the heap” is not an architectural answer.

Make the contract visible

Systems languages make that contract visible. References, boxes, moves, and destructors are architecture—not syntax trivia.

The API should expose the narrowest valid lifetime and ownership. Accept a borrow when retention is unnecessary, return ownership when the caller must control disposal, and allocate behind an arena when many objects share one bounded phase. Measure only after the contract is correct. An allocator benchmark cannot repair an API that permits dangling references, accidental clones, unbounded retention, or cross-thread sharing without a synchronization plan.

evidence ledger 1 claim
  1. sourced
    Rust ownership model

    The Rust Book makes ownership, moves, borrowing, and heap-backed smart pointers explicit in the type system.

    inspect source ↗
linked artifacts 1 attached
  • reference
    The Rust Programming Language: Ownership

    Public reference for moves, borrowing, and ownership-oriented lifetime reasoning.

    open ↗