I recently started learning Zig, and it just makes us think about memory very differently, genuinely.
In most languages, allocation is something that happens to you because the language runtime manages it. Zig has no hidden allocations. If a function needs heap memory, it has to take an allocator as a parameter (yes, a parameter).
So, just by reading the signature, I know the function allocates, and the caller decides where that memory comes from. Now, because the allocator is just a value you pass around, swapping strategies is easy.
For example, if I am handling a request with many small temporary allocations, I can pass an arena allocator. Everything allocated through it is released in one shot when the arena is destroyed. The function I call does not know or care. It only sees a generic allocator.
Cleanup is also explicit and sits right next to the allocation. `defer` frees memory when the scope ends, and `errdefer` frees it only if the function returns an error, so I do not have to remember every exit path.
My favorite part is testing this. There is a testing allocator that tracks every single allocation, and if a test finishes with memory that was never freed, the test fails. Leak detection becomes a normal test failure instead of something I discover in production.
Coming from garbage-collected languages or even extreme manually managed languages (like C), Zig makes us think about memory as a first-class citizen and be super thoughtful about it. Similar to what Go did with errors. Btw, Zig also does that :)
I am still early in the journey, but this alone has been worth the time.