A few months ago I got trapped into the Zig hype and decided to give it a try because I found its promised C interoperability and its efficiency very appealing. And they didn't lie!

I wrote a simple videogame in Zig and Raylib. If you want to see how the final result was, have a look at the link below!

When Zig is really a better C

Overall, Zig keeps the low-level control of C with an extra layer of zero-cost abstractions that remind me of Rust, with the simplicity and expressiveness of Go.

If I had to describe all the Zig features I like, I would probably need to write a book instead of a blog post. If I have to pick a single feature, I really love its type system: type definitions can be assigned to constants, which can be used to build other composite types or even generics without really requiring a special generics syntax. For example:

pub fn Box(T: type) type {
    return struct {
        value: T,
        pub fn new(init: T) Box(T) {
           return .{ .value = init };
        }
    };
}

The above code:

So if we wanted to create a generic boxed unsigned byte, you should do:

const boxed_int = Box(u8).new(123);

Box(u8) is a function call that returns a type, while new(123) is a member of the returned type, that returns an instance of such type.

If you felt curiosity, I really encourage you to learn more about the Zig language.

Another important aspect is about project ergonomics. Zig has learnt from languages like Go and Rust, and ships its own toolkit that standardizes code formatting, resolution of external dependencies and project structure. I've easily moved my project from one computer to another and I just needed to install a single tool: the zig CLI.

Integrating with external dependencies, even C dependencies, is so simple and straightforward that it is even simpler than integrating external C libraries in C projects. Especially if the C dependencies are built around build.zig, because the zig toolset has become a drop-in replacement of a C compiler and many pure C projects use it just as a compiler and a replacement for tools like cmake.

Rough edges

Zig is still under active development. The language is evolving from one minor version to another.

I'm also a Zig newbie so maybe there are elegant ways to overcome some of my concerns.

The biggest rough edge I found is, with the promised lack of undefined behaviors (this is another feature that should go to the Zig is a better C section), many operations must be explicit. Too explicit. For example, conversions between numeric types need verbose, explicit casts and divisions between integers require you to specify what to do with the remainder (truncate, round...). This excerpt from real code illustrates the math hell of Zig:

x = @floatFromInt(@as(c_int, @intCast(idx)) * font.baseSize);
y = @floatFromInt(@divTrunc(tile_index, tiles_width) * obj_size);

I would expect that in future versions, default numeric conversions and operations are defined and standardized so the above code could look like something simpler to understand, like:

x = idx * baseSize;
y = (tile_index / tiles_width) * obj_size;

Or at least a less verbose type casting system, like in Go:

x = float64(idx * baseSize)

I also found other operations too verbose, like, for every memory allocation, having to handle the logic of not having enough memory. When I run on a several-GB machine, I don't want to manually handle the out-of-memory error every time I dynamically allocate a few bytes.

The recurrent question: Zig or Rust?

I've heard this comparison multiple times in social networks but in my humble opinion I think this question is nonsense. They follow different paradigms and both languages operate at very different abstraction layers. Zig pretends to be a better C while Rust, somehow, also aims to be a better C++.

However, if you still keep this question in your mind, my response is: I'd choose Rust. Despite Zig being promising, Rust is already battle-tested in more scenarios, it has a bigger community and more complete tooling support.

During my brief-but-intense journey with Zig, I had to deal with incomplete/buggy IDE support and tools (there are WIP plugins for Visual Studio Code and Jetbrains' IDEs), some bugs in the compiler output, and breaking changes at a language level during minor version updates.

My adventure with Zig was fun, pleasant, creative and satisfying, but IMHO it is still work-in-progress, and adopting it for maintaining a project comes with some long-term maintainability risks.

Is then Zig production-ready or not?

I know some big projects written in Zig that have reached production, for example Bun is a blazingly fast Javascript Runtime that was written originally in Zig until its version 1.4.0, when Bun was completely rewritten to Rust, for reasons that are not related to the language stability but to the maintainability of a critical concurrent, garbage-collected runtime with millions of lines of code.

The OpenTelemetry injector runs in critical infrastructures and enables standardized application-level monitoring in multiple runtimes and languages. It is a small-mid size project and they chose Zig, according to Michele Mancioppi, one of its maintainers:

we needed an SDK that did not depend on LibC: if the injector depended on any LibC flavor, even if statically compiled, it would crash processes linking to a different LibC flavor.

These are only two of many examples empirically demonstrating that Zig is being shipped in production (together with other notable mentions like Ghostty or TigerBeetle), so I won't be the guy affirming that Zig is not production-ready because it is actually in production.

However I have the feeling that Zig is going steadily, and its sudden and meteoric success came with an overwhelming amount of petitions, bug reports and the added extra work of managing a growing and successful open source community. Zig is backed by the Zig Software Foundation, with some companies providing financial support but I guess it is still far from the financial muscle of other language maintainers like Rust (the Rust foundation) or Go (Google).

Summarizing

Give Zig a try, enjoy the process, and decide by yourself. I really loved it and I think once it reaches its first 1.0 stable version, it will be a serious option for small-to-medium sized projects requiring a minimal footprint.

But being honest and realistic, for the kind of projects that we average mortals are involved in, less-fancy languages like Go, C# or Java are usually the safest option 😃