Zig is a systems programming language created by Andrew Kelley in 2015 for the same reason that all good tools are created: because the existing ones were unacceptable and the person who noticed was stubborn enough to build a replacement.
C was fast and dangerous. C++ was fast, dangerous, and complicated. Rust was safe but required a three-week negotiation with the borrow checker to allocate a string. Go was productive but had a garbage collector, which in systems programming is like having a housekeeper who rearranges your furniture while you’re performing surgery.
Zig is the language that looked at this landscape and asked: what if we trusted the programmer? Not the way C trusts the programmer — which is to say, the way a casino trusts a gambler, by letting them do anything and profiting from the inevitable mistakes. But genuinely trusted them: made every dangerous operation explicit, made every allocation visible, made every cost legible, and then got out of the way.
“No hidden control flow. No hidden allocations. No hidden anything.”
— The Zig philosophy, which fits on a sticky note, unlike the Rust borrow checker documentation
What C Should Have Been
Zig is what C would be if C were designed today by someone who had spent thirty years watching C programs crash, overflow, and invoke undefined behavior in ways that the C standards committee defends as “implementation-defined” with the serene confidence of a priest explaining why suffering is a feature.
The corrections are surgical:
Undefined behavior becomes safety-checked. In C, signed integer overflow is undefined behavior — the compiler is allowed to do anything, including delete your code, because the standards committee decided in 1989 that optimisation was more important than predictability. In Zig, integer overflow is a detectable illegal behavior in safe mode and wrapping arithmetic is available via explicit operators (+%, -%). You can have the dangerous thing. You just have to ask for it by name.
Allocations are explicit. Every function that allocates memory receives an allocator as a parameter. This is Zig’s most distinctive design choice and its most controversial. A Rust programmer wraps allocations in smart pointers. A Go programmer lets the garbage collector handle it. A Zig programmer passes an allocator to every function that needs one, the way a responsible adult brings their own bags to the grocery store. It is more verbose. It is also the reason that a Zig program’s memory behavior is entirely visible from reading the code — no hidden allocations, no hidden garbage collector pauses, no hidden anything.
No hidden control flow. In C++, a line like a = b + c; might invoke three operator overloads, two implicit conversions, a copy constructor, and a move constructor. In Zig, a = b + c is addition. If you want something else, you write something else. The code is what it does. The code does what it says.
No preprocessor. C’s preprocessor is a separate text-manipulation language that runs before compilation, produces errors that reference lines that don’t exist in your source file, and has been responsible for more debugging hours than all other C features combined. Zig replaced it with comptime — compile-time execution of regular Zig code, using the same syntax, the same semantics, and the same debugger. The metaprogram is the program. The program is the metaprogram.
Comptime
comptime is Zig’s answer to C macros, C++ templates, Rust’s procedural macros, Go’s go generate, and every code generator ever written in Python. The answer is: just run the code at compile time.
fn fibonacci(comptime n: u32) u32 {
if (n < 2) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// This computation happens at compile time.
// The binary contains only the result.
const fib_10 = fibonacci(10);
This is not a macro. It is not a template. It is Zig code, running at compile time, using the same language, the same type system, the same control flow as runtime code. The result is embedded in the binary as a constant. The compiler is the interpreter. The interpreter is the compiler.
A C programmer would generate this with a Python script that outputs a header file. A C++ programmer would write a recursive template that compiles in O(2^n) time and produces error messages longer than the program. A Rust programmer would write a procedural macro that requires a separate crate, a syn parser, and a prayer. A Zig programmer writes a function and puts comptime in front of the parameter.
The Lizard has not commented on comptime. The Lizard does not comment on things that are obviously correct.
The Build System That Is the Language
Zig’s build system is written in Zig.
This sentence does not sound revolutionary until you have spent time with CMake — a build system written in its own domain-specific language, which is itself configured by a generator, which produces Makefiles or Ninja files or Visual Studio projects depending on the platform, the phase of the moon, and how much the developer has offended the CMake gods this week.
Or with Cargo — Rust’s build system, which is excellent until you need to link a C library, at which point you write a build.rs file in Rust that shells out to cc or pkg-config and hopes for the best.
Zig’s build system is a Zig program. It uses Zig’s package manager. It links C libraries natively because Zig includes a C compiler. It cross-compiles for six architectures by changing a flag. It does not have a domain-specific language. It does not have a generator. It does not have a phase of the moon.
The build system is boring. The build system works. Go programmers recognise this energy.
The Cross-Compilation Story
Zig can cross-compile for any target that LLVM supports, including C and C++ code, because Zig ships a C compiler as a subcomponent.
zig build -Dtarget=aarch64-linux-gnu
zig build -Dtarget=x86_64-windows-msvc
zig build -Dtarget=riscv64-linux-musl
This means a Zig developer on a Mac can compile a C program for a RISC-V Linux board in one command. A C developer attempting the same task will spend a day setting up a cross-compilation toolchain, installing the correct sysroot, configuring pkg-config paths, and wondering why libpthread is missing on a platform that definitely has threads.
Zig’s cross-compilation is so good that people use Zig as a C cross-compiler without writing any Zig. zig cc is a drop-in replacement for gcc or clang that cross-compiles without configuration. This is the most Zig thing possible: the language is so well-engineered that its best-known feature is not the language itself but the C compiler it happens to include.
Why It’s Not Rust
Rust and Zig occupy similar territory — systems programming without garbage collection — but they made opposite philosophical choices:
Rust chose to prevent mistakes at compile time. The borrow checker enforces memory safety through a type system that tracks ownership, lifetimes, and mutability. The compiler will not let you write unsafe code unless you explicitly opt in with unsafe. The cost is developer time: the borrow checker adds a negotiation step between “I know what I want” and “the compiler agrees I know what I want.”
Zig chose to make mistakes visible. There is no borrow checker. There is no ownership system. There are no lifetimes. You can have a pointer. You can dereference a pointer. You can have a dangling pointer, and the safety-checked runtime will catch it in debug mode, and in release mode you have chosen to accept the consequences. The cost is runtime safety in release builds: Zig programs can segfault. Rust programs (without unsafe) cannot.
The Rust programmer says: “the compiler should prevent me from making mistakes.”
The Zig programmer says: “the compiler should make my mistakes visible so I can fix them.”
The C programmer says: “what mistakes?”
The Go programmer says: “the binary shipped yesterday.”
Both Rust and Zig are correct. They are correct for different people and different problems. The difference is not technical. It is temperamental. Some people want a safety net. Some people want a clean view of the ground.
riclib’s Next Language
riclib reads the Zig documentation the way a happily married man browses real estate listings in another country — not planning to move, just appreciating the architecture.
The appreciation is genuine. Zig’s philosophy — explicit allocations, no hidden control flow, comptime instead of code generation, the build system as a program — is the bootblock philosophy. It is 488 Bytes thinking applied to a modern systems language. Every byte accounted for. Every operation visible. Every cost paid consciously.
riclib is not learning Zig because riclib is shipping in Go, and Go compiles, deploys, and runs, and the binary works, and the binary has always worked. The twenty-four hours in a day are the constraint, and Go fills them with production code, not learning curves.
But.
If Go didn’t exist. If riclib were starting today. If the question were “what language thinks the way the demo scene taught me to think” — the answer would be Zig. Not Rust, which thinks the way a type theorist taught a compiler to think. Not C, which thinks the way a PDP-11 taught Ken Thompson to think in 1972 and has refused to reconsider since. Zig, which thinks the way a systems programmer thinks after watching forty years of systems programming mistakes and deciding to fix them without adding a borrow checker, a garbage collector, or a PhD prerequisite.
The iguana on the Zig workbench is not the Lizard. But the Lizard recognises it. They blink in the same rhythm.
“If I had time, I’d write everything in Zig. I don’t have time. That’s why I write in Go.”
— riclib, being honest about the trade-off
Andrew Kelley
A note about Andrew Kelley, because the man deserves it.
Zig was built by one person. Not by a committee at Google (Go). Not by a research team at Mozilla (Rust). Not by a standards body spanning forty years (C). By one developer who livestreamed the compiler development, explained every decision, responded to community feedback, and built a language that competes with decades-old ecosystems because the engineering is that good.
This is the Solo Developer energy that the lifelog mythology respects — the same energy as the boy with the Amiga 500 who made six layers of parallax stars in 488 bytes. One person, one problem, no committee, no compromise.
The Zig Foundation now has a team. The ecosystem is growing. But the origin is pure: one person who had enough of C’s undefined behavior, enough of Rust’s compile times, enough of Go’s garbage collector, and enough stubbornness to build an alternative.
Measured Characteristics
- Year created: 2015
- Creator: Andrew Kelley (one person)
- Hidden control flow: 0
- Hidden allocations: 0
- Hidden anything: 0
- Preprocessor: replaced by
comptime(same language, same debugger) - Build system language: Zig (no DSL, no generator, no CMake)
- Cross-compilation targets: every LLVM target (one command)
- C compiler included: yes (and people use it just for that)
- Borrow checker: none (the programmer is trusted)
- Garbage collector: none (the programmer is responsible)
- Lines of code to explain
comptime: fewer than C++ templates - Lines of code to explain C++ templates: more than
comptime - riclib Zig projects shipped: 0 (too busy shipping Go)
- riclib Zig documentation tabs open: several (recurring)
- Iguanas recognised by the Lizard: 1
- The code is what it does: always
