Combined with typed fat pointers (slices and strings), typed hashmaps and stack-trace-assertions, C in general becomes quite nice. The rest is compiler flags.
Go solves this by being a better language out of the box, but with the Wirthian aspects removed they feel very similar. Perhaps not so surprising.
Arenas are great for avoiding allocations per tick/request/frame/layer. No symmetric free() to bracket lifetimes! They have a purpose, and we always knew that.
But by definition, your program is over-allocating as a tradeoff. Makes a ton of sense in certain use cases. However, we didn’t invent garbage collection and borrow-checking and realloc() just to publish papers ;)
Half of my time programming zig is spent considering allocation strategies. That’s a feature. “Where are the bytes?”
So, maybe we could also have allocator bubbles, where code running inside the bubble is just as allocator-naive as any other Go code, but when it allocates (make/new/pointer escape), it uses the bubble's allocator instead of the default one.
The big problem I can think of, though, which doesn't apply to time bubbles, would be that pointers drawn from that allocator might escape the bubble (e.g. by assigning them to a longer-lived struct field). It's possibly something that can be detected by the runtime, but because the API is non-invasive by design, it's not something that will be apparent to the programmer when looking at the code.