Zig’s Comptime and the Case Against Hidden Control Flow
Metaprogramming without a second language: no preprocessor, no macros, no separate mental model to learn.
Zig’s own homepage states its design philosophy in one blunt sentence: “There is no hidden control flow, no hidden memory allocations, no preprocessor, and no macros.” That’s not marketing copy so much as a direct rebuke of two things most systems programmers have simply learned to live with, C’s text-substitution preprocessor and the macro systems that grew up around languages that came after it. Andrew Kelley started Zig on February 8, 2016, with a specific complaint: too much of what a program actually does is invisible at the point where you’re reading it. Comptime, Zig’s mechanism for running code at compile time, is the alternative Zig built instead of adding a macro system, and it’s worth understanding on its own terms rather than as a footnote to Rust or C.
What “Hidden Control Flow” Actually Rules Out
The manifesto is more specific than it sounds. Zig has no operator overloading, so a + b can never secretly call a user-defined function the way it can in C++ or Rust; if you see a function call in Zig, a function is being called, and if you don’t, one isn’t. Zig has no exceptions, so there’s no stack unwinding happening somewhere you can’t see in the source; errors are ordinary values wrapped in an error union type, and propagating one requires the visible try keyword at every single call site. Allocations work the same way: there is no global allocator quietly backing your data structures, every function that needs to allocate takes an allocator as an explicit parameter, so a memory allocation is a line of code you can point to, not an implementation detail buried three layers down a call stack.
Comptime: One Language, No Separate Dialect
Where C reaches for the preprocessor and Rust reaches for macro_rules! or a procedural macro crate, Zig reaches for comptime, and the entire pitch is that comptime code is just Zig code, evaluated by the compiler instead of the CPU. The same syntax, the same type system, the same error messages. A generic function looks like this:
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
type is a first-class value in Zig, so comptime T: type is an ordinary parameter, just one the compiler resolves before code generation rather than at runtime. There’s no separate template syntax to learn the way there is in C++, and no attribute macro expanding into code you have to run a separate tool like cargo expand to actually see. What you read is what runs, just at a different time.
| C preprocessor | Rust macros | Zig comptime | |
|---|---|---|---|
| Operates on | Raw text, before parsing | Token trees (declarative) or a separate program (procedural) | Ordinary, typed Zig code |
| Type-checked | No | Only after expansion | Yes, the same as runtime code |
| Separate syntax to learn | Yes, its own mini-language | Yes, matcher syntax or the proc-macro API | No, it’s Zig |
| Debuggable with normal tools | Rarely, errors point at expanded text | Often not without extra tooling | Yes |
Where the Borrow Checker Doesn’t Fit This Story
It’s tempting to place Rust’s borrow checker in the same “hidden” category as macros, but it’s a different kind of thing entirely, and Zig’s manifesto doesn’t really compete with it. The borrow checker isn’t generating code or expanding text, it’s a static proof that runs silently over your whole program to guarantee no two mutable references to the same data can exist at once, a genuinely powerful piece of compiler reasoning that happens without you writing anything extra. Zig opts out of that category of power altogether rather than offering a competing version of it. Memory safety in Zig leans on runtime checks, bounds checks, checks for use of undefined memory, that are present in Debug and ReleaseSafe builds and compiled out in ReleaseFast and ReleaseSmall, a trade-off the language is explicit about rather than something it tries to hide. Kelley has put the positioning plainly: “Zig is not trying to be Rust. Zig is trying to be a better C.” The comparison that actually holds is narrower than “which language is safer,” it’s “which language makes you learn a second dialect to do compile-time work,” and on that specific question C and Rust both answer yes, while Zig answers no.

Zig, still pre-1.0, posts an admiration score of 64.2 percent among the developers who’ve actually used it, edging out Elixir and trailing only Rust among languages in this comparison. That’s the pattern usage data tends to show right before a language’s adoption curve steepens, a small group of people who’ve tried it liking it disproportionately more than the general population has heard of it.
Real Momentum, Not Just Enthusiasm
The clearest evidence that Zig has moved past hobbyist curiosity is who’s paying for it. Bun, the JavaScript runtime built substantially in Zig, and TigerBeetle, a financial transaction database, each donated $60,000 to the Zig Software Foundation in 2024, the kind of check a company writes when a language is load-bearing infrastructure, not a side experiment. ZML, an inference platform, gave $33,000. HashiCorp co-founder Mitchell Hashimoto personally pledged $300,000 spread across 2024 and 2025. None of that money came from venture capital or a corporate parent; the Zig Software Foundation, a nonprofit Kelley founded in 2020, runs entirely on donations and reported total 2024 income of $670,672.59 against expenses of $520,748.91.

The clearest recent signal of a community large and confident enough to act on its own principles came in November 2025, when Zig moved its entire canonical repository off GitHub to Codeberg, a nonprofit git host, with no mirror left behind, citing GitHub Actions reliability problems and a conflict with Zig’s policy against LLM-generated contributions. At the time of the move the repository carried more than 43,000 stars and over 3,000 forks. Walking away from that kind of visibility on the platform most open-source discovery still runs through is not a decision a small, uncertain project makes lightly.
The Manifesto Has a Cost
None of this makes comptime free of trade-offs. Zig’s refusal to add operator overloading or a trait-like constraint system for generic parameters means comptime type errors can surface as long, unfamiliar compiler output when a type doesn’t support an operation deep inside a generic function, a complaint with real precedent in C++ template error messages before that language added concepts. Zig accepts that cost deliberately: the alternative, some kind of constraint or interface system layered on top of comptime, would itself be a small addition to the language’s surface area that the “no hidden anything” philosophy is built to avoid.
When comptime’s philosophy actually fits your project
- You want generics, conditional compilation, and code generation to use one syntax, not three
- Reading a function call and trusting that it’s really just a function call matters more than operator sugar
- You’re comfortable trading compile-time aliasing guarantees for explicit, visible allocation and error handling
- You can tolerate a pre-1.0 language, current releases are still in the 0.x beta series, in exchange for a smaller, more legible mental model than C’s preprocessor or Rust’s macro system
What We Learned
Zig’s “no hidden control flow” manifesto and its comptime mechanism are the same idea applied consistently: metaprogramming, generics, and conditional compilation should use the language you already know, not a second one bolted on beside it. That’s a genuinely different answer to compile-time flexibility than C’s preprocessor, which operates on raw text before the compiler even understands types, or Rust’s macro system, which trades that same problem for a token-tree or proc-macro API most developers only partially internalize. It’s also a narrower answer than Rust’s borrow checker, which Zig doesn’t compete with at all, opting for visible runtime checks over an invisible, if powerful, static proof.
The real evidence Zig’s bet is paying off isn’t the manifesto itself, it’s that companies with production systems riding on the language, Bun and TigerBeetle among them, are now paying its nonprofit foundation’s bills, and that the community was confident enough to walk away from GitHub entirely rather than compromise on its principles.


