ArenaAllocator in Zig is a tool to help managing allocating and freeing memory in a group manner. It’s not just like what many articles tell you that Arena will allocate a lot of times and only free once. This gives people an impression that they will have one Arena on the top of their program, then only free when the program shutdown. That’s not true and that’s not how most Zig programmers use Arena.
People usually let their structs own their own arena. Each struct has its own ArenaAllocator. They have a function .init(whatever_child_allocator) !WhatEverStruct… So in this case, if their struct alive, all the heap data for the struct, doesn’t matter if they are in fields or not, will be alive. When their struct’s lifetime is ended, they call deinit(), in the deinit() function, the Arena will deinit(), and all the memory pages associated with the struct return back to the system. By using this way, you reduce the abusing of defer allocator.free(blabla), because you group the data together. You still defer deinit() all the data when you don’t need them, but you write less defer deinit() or free() codes, so you will less likely to forget. This is not a longer lived leaking. Sometime, you just delayed the free for nano seconds…
Arena Allocator in Zig can also be reset with retain capacity. The memory is not free but they will be reused. This is particular helpful when your program has a main loop (or multiple infinite loops in multi threading program), and your structs with Arena are being used in the loop. By the end of each loop, you reset(.retain_capacity) the Arena instead of deinit() it. In the next loop, the Arena will reuse the memory, and it only alloc more memory if the next loop need more memory then the previous loop. For example, you develop a website that allow users to post article. Everytime an user click the Submit button, you need some memory to store the article for some tasks, formatting, rendering, saving to the disk. After that, your Arena resets the memory instead of free them. So when next user post another article, if the article is shorter, it will never need to alloc, so no heap syscall is needed. This is very convenient and helpful when you are sure you need to frequently do something (a loop), with an almost known max size of memory. Like the article, you expect every user will not write a supper long article. But what if some users still write some super long articles, but most users don’t. Can we have a way to return the pages back to the system? Yes. The reset function of the ArenaAllocator also has a option called retain_with_limit: usize. This means that you can only retain a certain amount of the memory, and free the rest. For example, you estimate that most of the users’ articles will not be more than 4096 bytes, so after every submit, you call arena.reset(.retain_with_limit: 4096). If occasionally a crazy guy write 5096 bytes, it will release the 1000 bytes back to the system. So in this way, it guarantees that your memory will not continually grow.
These are 2 major examples that how people usually use Arena here.
In Rust community, those data driven people using their own Generational Arena or IndexTree to fool the borrow checker is essentially doing the similar thing (this is why they also call it “arena”). Or Rc and Arc people just make Rust to a GC language. Even by then, they still need to group something together to reduce the IndexTree and/or Rc… And functional people doing HttpClient.add_header().add_body().add_url().add_extra_header().add_port().set_timeout().send().unwrap()… this just abusing the clone(), how many times you call the functions, how many full struct clone(), which also mean alloc the whole struct and free the old whole struct syscall, will happen. When your struct includes heap fields, such as String, Vec, HashMap, etc., you can’t copy the struct in Rust, and you need to add the clone to derives. When you do this kind of functional style programming, they are essentially just like a struct with an allocator, then allocator will create a new struct then destroy the old one
I think Rust and Zig and even C/C++ will cross the path eventually if people really want to write some good programs. They are basically doing the same thing. After doing a lot of C and Zig, when I see people’s Rust codes, I know what is under the hood… (many times it is in a good way though, not necessary to be negative.)