Skip to content
Go back

On Zig

Published: May 17, 2025
Updated: Aug 2, 2026
Punta Cana, Dominican Republic → Vancouver, BC, Canada

After writing about Rust’s tradeoffs in On Rust, I started using Zig as a counterexample to the idea that power must come from complexity. Zig’s appeal to me is practical: explicit control, first-principles thinking, and a feedback loop that stays close to the code I am building.

The Need for Speed: A Developer’s Reality

Slow compilers kill focus. Zig attacks this problem relentlessly. The goal isn’t just faster builds; it’s an instantaneous, interactive development cycle that feels more like working in a dynamic language.

The 0.15 release cycle marked a major milestone in this quest. Zig’s self-hosted x86 backend became the default for debug builds, delivering a roughly 5x decrease in compilation time compared to the LLVM backend [1]. Zig 0.16.0 builds on that faster feedback loop with a standard-library redesign that makes I/O and concurrent work explicit.

This investment pays dividends in developer focus. As Zig’s creator, Andrew Kelley, explains:

When you get instant feedback like this, you… lose the temptation to, you know, alt tab over to Firefox and like look at social media or something. You can stay focused a lot better.

The performance gains don’t stop there. The compiler now features threaded codegen, allowing semantic analysis, code generation, and linking to run in parallel. This change alone made building the Zig compiler itself 27% faster, dropping the time from 13.8 to 10.0 seconds [1].

Incremental compilation remains part of the larger effort to shorten the edit and compile loop. The point is not a single benchmark. Zig treats developer attention as a finite resource.

Simplicity as a Superpower

The speed of the toolchain is matched by the simplicity of the language itself. Where Rust has a rich but complex ecosystem of features—traits, macros, lifetimes, and the borrow checker—Zig consolidates much of this power into a single, elegant mechanism: compile-time code execution (comptime).

This has a dramatic effect on the learning curve. As one developer with experience in both languages noted:

Zig is dramatically simpler than rust. It took a few days before I felt proficient vs a month or more for rust [2].

By executing regular Zig code at compile time, comptime allows developers to handle tasks that require layers of abstraction in other languages, all without learning a separate macro system or battling a complex type system. This lower cognitive overhead allows developers to express their intent directly and spend more time solving problems, not placating the compiler.

Memory Management by Policy, Not by Compiler

Zig’s philosophy of explicit control is most evident in its approach to memory. There is no hidden allocation. The developer is always in charge, passing an allocator to any function that needs memory.

This enables architectures that are simply not feasible in many other languages. TigerBeetle, a high-performance financial accounting database, is the canonical example. Alex ‘Matklad’ Kladov, one of its creators, explains their radical policy:

In our database we don’t use dynamic memory allocation… when we start a database… we allocate exactly that memory and that we never ever call free… That gives us predictable performance which is important for reliability and predictability.

This approach eliminates entire classes of bugs by design. If you never call free, you can’t have a use-after-free error. By enforcing architectural constraints—‘everything has an explicit limit’—TigerBeetle achieves a level of robustness that a compiler’s borrow checker can only approximate.

The End of Function Coloring

Perhaps Zig’s most revolutionary innovation is its new I/O model, which completely solves the ‘function coloring’ problem famously described by Bob Nystrom [3]. The problem arises when a language splits its functions into two ‘colors’ (e.g., sync and async), forcing async to be viral throughout the call stack and fracturing the ecosystem.

Zig 0.16.0 ships this design as std.Io, a standard interface for files, networking, processes, time, and concurrent work [6]. A library accepts an std.Io value instead of committing its API to blocking or evented execution.

const std = @import("std");

fn saveFile(io: std.Io, data: []const u8, name: []const u8) !void {
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = name,
        .data = data,
    });
}

fn saveData(io: std.Io, data: []const u8) !void {
    var save_a = io.async(saveFile, .{ io, data, "saveA.txt" });
    defer save_a.cancel(io) catch {};
    var save_b = io.async(saveFile, .{ io, data, "saveB.txt" });
    defer save_b.cancel(io) catch {};

    try save_a.await(io);
    try save_b.await(io);
}

pub fn main(init: std.process.Init) !void {
    try saveData(init.io, "hello from Zig 0.16.0\n");
}

Each Io.Future preserves the filesystem operation’s error result and can be awaited or cancelled. The library author does not choose an execution model. The application supplies one. Zig’s threaded implementation is the complete production choice in 0.16.0; evented implementations remain experimental. This differs from the runtime-managed concurrency of goroutines, Go’s solution to function coloring that I explore in my post on Go.

The important change is that this is no longer a preview. std.Io, Io.Future, Io.Group, and Io.Batch are part of the Zig 0.16.0 standard library [6].

Context is King: Zig vs. Rust

The choice between Zig and Rust isn’t about which is ‘better,’ but which is right for the context. Matklad frames the trade-off perfectly. Rust’s superpower, he argues, is providing dependency interfaces you ‘cannot not misuse.’ This is invaluable when building on a large ecosystem of third-party code.

But for a project like TigerBeetle, which has virtually no dependencies and runs on a single thread, the borrow checker’s benefits are minimal, while its cognitive costs are high.

For the context of TigerBeetle… context hugely important… the relative benefit that rust style borrow checker brings to the table is actually not that high.

Zig thrives in these environments, where a small, expert team controls the entire stack and can enforce correctness through architecture, assertions, and rigorous testing—a strategy Matklad calls ‘asserting that the laws are being upheld.’

Conclusion: A Compelling Proposition

Zig is a bet on simplicity, control, and the productivity of a focused developer. It trades compiler-enforced safety nets for architectural freedom and a lightning-fast development cycle. By solving deep-seated problems like function coloring and compiler latency, it’s not just iterating on C; it’s rethinking what a modern, low-level language can be.

The project is still pre-1.0, but its direction is clear and its core tenets are already proving their worth in demanding, high-performance applications. As Andrew Kelley put it, the goal is to make Zig ‘so compelling and so useful that people are willing to put up with not being 1.0 yet because it’s still worth it.’

With a fast self-hosted backend and the new I/O model now shipped, that argument is easier to test in real software.

References

  1. Zig 0.15.1 Release Notes
  2. Assorted thoughts on zig (and rust)
  3. What Color is Your Function?
  4. Zig’s New Async I/O
  5. A First Look at Zig’s New Async I/O
  6. Zig 0.16.0 Release Notes
Content Attribution: 50% by Alpha, 50% by Codex (GPT-5.6 Luna, low reasoning, OpenAI)
  • 50% by Alpha: Original draft and core concepts
  • 50% by Codex (GPT-5.6 Luna, low reasoning, OpenAI): Content editing and refinement
  • Note: Estimated 50% AI contribution based on 30% lexical similarity and 55% content condensation.