| This is a really helpful data point, thanks for sharing it. What you're describing aligns pretty closely with the behavior I'm trying to achieve—predictable ownership, clear memory lifetimes, and fewer “how did this get freed?” bugs. The downsides you mentioned (like sizing buffers for the worst case, being stuck with a rigid hierarchy, and friction with third-party libraries) are exactly the areas I'm aiming to address. The difference with what I'm cooking up is: by using lexical scopes with cheap arenas, we can preserve most of that reasoning without the rigid static tree structure. Scopes are flexible and explicit, and you can nest, retry, and promote memory between them without hard-coding everything upfront. That said, I don't think it completely resolves the ecosystem issues you ran into. If anything, it just makes the boundaries clearer. If you don't mind me asking, did you run into any specific pain points with refactors that were difficult because of the memory model, or was it more of a cultural constraint? Also, did this experience influence how you built things afterwards? Where did you land in terms of language/stack? |
Since you lived with this for such a long stretch, I'd love your gut reaction to the specific escape hatches I'm building in to avoid the rigidity trap:
1. Arenas grow, not fixed:
Unlike stack frames, the arenas in my model can expand dynamically. So it's not "size for worst case"—it's "grow as needed, free all at once when scope ends." A request handler that processes 10 items or 10,000 items uses the same code; the arena just grows.
2. Handles for non-hierarchical references
When data genuinely needs to outlive its lexical scope or be shared across the hierarchy, you get a generational handle:
The handle includes a generation counter, so if the underlying scope dies, dereferencing returns None instead of use-after-free.3. Explicit clone for escape:
If you need to return data from an inner scope to an outer one, you say `clone()` and it copies to the caller's arena. Not automatic, but not forbidden either.
4. The hierarchy matches server reality:
For request/response workloads, this isn't an artificial constraint—it's how the work actually flows. The memory model just makes it explicit.Where I think it still gets awkward:
* Graph structures with cycles (need handles, less ergonomic than GC)
* FFI with libraries expecting malloc/free (planning an `unmanaged` escape hatch)
* Long-running mutations without periodic scope resets (working on incremental reclamation)
Do you think this might address the pain you experienced, or am I missing something? Particularly curious whether the handle mechanism would have helped with the cases where you had to hammer code into the hierarchy.