Dayvi Schuster 8 min read Tuesday, September 23, 2025 Why Zig Feels More Practical Than Rust for Real-World CLI Tools A deep dive into the practical advantages of Zig over Rust for building command-line interfaces.
Introduction
So when it comes to memory management there are two terms you really need to know, the stack and the heap.
The stack is a region of memory that stores temporary data that is only needed for a short period of time. It operates in a last-in, first-out (LIFO) manner, meaning that the most recently added data is the first to be removed, as the name suggests. Basically imagine a stack of plates, if you wanna remove one plate you remove the top one, remove the middle plate and disaster awaits in this analogy. The stack is typically used for storing function parameters, local variables, and return addresses. It is fast and efficient because it has a fixed size and does not require dynamic memory allocation.
The size of the stack is usually limited, and if a program tries to use more stack space than is available, it can result in a stack overflow error. This can happen if a function calls itself recursively too many times or if a program allocates too much memory on the stack.
Whereas the heap as the name suggests is a region of memory that is used for dynamic memory allocation. Unlike the stack, the heap does not have a fixed size and can grow or shrink as needed. The heap is typically used for storing data that needs to persist beyond the lifetime of a single function call, such as objects or data structures that are created at runtime. Imagine the heap as a pile of clothes in a disorganized household, you can add or remove clothes as needed and as long as the pile isn’t too big you can find what you need with relative speed and ease. But it will quickly become a nightmare if you let it grow out of control. The heap is managed by the operating system and requires dynamic memory allocation, which can be slower and less efficient than stack allocation.
The heap can also become fragmented over time, since we do not always store data in a contiguous block of memory. This can lead to performance issues and make it more difficult to allocate large blocks of memory.
Rust’s Borrow Checker
Rust’s borrow checker is a a pretty powerful tool that helps ensure memory safety during compile time. It enforces a set of rules that govern how references to data can be used, preventing common programming memory safety errors such as null pointer dereferencing, dangling pointers and so on. However you may have notice the word compile time in the previous sentence. Now if you got any experience at systems programming you will know that compile time and runtime are two very different things. Basically compile time is when your code is being translated into machine code that the computer can understand, while runtime is when the program is actually running and executing its instructions. The borrow checker operates during compile time, which means that it can only catch memory safety issues that can be determined statically, before the program is actually run.
This means that basically the borrow checker can only catch issues at comptime but it will not fix the underlying issue that is developers misunderstanding memory lifetimes or overcomplicated ownership. The compiler can only enforce the rules you’re trying to follow; it can’t teach you good patterns, and it won’t save you from bad design choices.
... continue reading