There's a lot of hate in these comments. Nobody is forcing you to use Zig and it's not trying to be "done" right now. And in fact, if the only thing they were focusing on was putting a bow on the project to call it "1.0", it probably wouldn't achieve any of it's long term goals of being a mainstream systems programming language. If it takes another five years or fifteen, as long as the project moves forward with the same energy, it's going to be fine.
For a fairly small project that's largely one dude, this is far more than most of us have or could hope to ever achieve ourselves. Give the people putting in the work credit where credit is due.
Right now there is no language that is good at io-uring. There are ok offerings, but nothing really has modern async joy that works with uring.
Whoever hammers out a good solution here is going to have a massive leg up. Rust is amazing in so many ways but it has been quite a brutal road to trying to support io-uring ok, and efforts are still a bit primitive, shall we say. If Zig can nail this down that would be fantastic!!
I would way rather Zig keep learning and keep changing, keep making new and better. Than to have it try to appease those who are too conservative for the project, unwilling to accept change and improvement, people focused on stability. It takes a lot of learning to make really good systems, to play with fit and finish. Zig is doing the good work. Imo we ought be thankful.
(The green threads coro stack stuff makes this more important.)
[1]: https://github.com/ziglang/zig/issues/23475#issuecomment-279...
That thing, right here, is probably going to be rewritten 5 times and what not.
If you are actively using Zig (for some reasons?), I guess it's a great news, but for the Grand Majority of the devs in here, it's like an announcement that it's raining in Kuldîga...
So m'yeah. I was following Zig for a while, but I just don't think I am going to see a 1.0 release in my lifetime.
It might be because I've done it a few times now, and/or because of the existence of LLMs, but this is the most fun I've had doing, and the most productive I've been, and the engine absolutely rips performance wise.
Zig makes it very easy to do this kind of lowish-level data-oriented programming, and tbh, I'm hooked. I was using rust for my performance critical services but dancing around the strictness and verbosity of memory management in rust gives me nothing in comparison and just gets in my way. This is partially a skill issue, but life is short and I just want to make fast, well organized software that works.
> They are now available to tinker with, by constructing one’s application using std.Io.Evented. They should be considered experimental because there is important followup work to be done before they can be used reliably and robustly:
And then they proceed to list 6 important pending work to be done.
And tbh, I take a 'living' language any day over a language that's ossified because of strict backward compatibility requirements. When updating a 3rd-party dependency to a new major version it's also expected that the code needs to be fixed (except in Zig those breaking changes are in the minor versions, but for 0.x that's also expected).
I actually hope that even after 1.x, Zig will have a strategy to keep the stdlib lean by aggressively removing deprecated interfaces (maybe via separate stdlib interface versions, e.g. `const std = @import("std/v1");`, those versions could be slim compatibility wrappers around a single core stdlib implementation.
True in general but in the cloud especially, saving server resources can make a significant impact on the bottom line. There are not nearly enough performance engineers who understand how to take inefficient systems and make improvements to move towards theoretical maximum efficiency. When the system is written in an inefficient language like Python or Node, fundamentally, you have no choice but to start to move the hotpath behind FFI and drop down to a systems language. At that point your choices are basically C, C++, Rust, or Zig. Of the four choices, Zig today is already simplest to learn, with fewer footguns, easier to work with, easier to read and write, and easier to test. And you're not going to write 100k LOC of optimized hotpath code. And when you understand the cost savings involved in reducing your compute needs by sometimes more than 90% by getting the hotpath optimized, you understand that there is very much indeed a business case to learning Zig today.
People see the languages/libraries they use as their sellable articles. And why wouldn’t they? Every job application begins with a vetting of which “tools” you can operate. A language, as a tool, necessarily seeks to grow its applicability, as securing the ROI of if its users.
And even when not tied to direct monetary incentives, it can still be tied to one’s ability to participate and influence the direction of various open source efforts.
Mix in barely informed decision makers, seeking to treat those engineers as interchangeable assets, and the admirable position being promoted above falls down the priority chain.
Though perhaps the Zig developers have promised this will not happen.
This would translate as ~"eats pussy", where "broûter" is a verb reserved for animals feeding on grass, implying a hefty bush.
It's another language stack that would need to be maintained within Linux distributions for years to come (security support, architecture support etc).
Upstream developers always seem to assume that there is no cost associated to introducing new software stacks. But in the end, someone has to maintain it. And they keep forgetting the purpose of software is to serve users, not developers.
And I'm not sure what's so revolutionary about Zig that couldn't have been solved by improving other languages.
For Zig in particular, the language isn't even stable enough that you can compile packages like Ghostty with any recent version of the Zig compiler. It has to be a very specific version of the compiler.
> You don't need to treat it like an identity.
This is an eternal problem in this industry and it is by far the most annoying thing about it.
If you want to compete with C, you can't do so without understanding that its stability and the developers focusing on mastering its practices, design, limitations, tooling has been one of the major successes.
With the exception of fewer foot guns, which Rust definitely takes the cake and Zig is up in second, I'd say Zig is in last place in all of these. This really screams that you aren't aware of C/C++ testing/tooling ecosystem.
I say this as a fan of Zig, by the way.
If it’s a compiler frontend-> LLVM interaction bug, I think you are commenting in the spot - it should go in a separate issue not in the PR about io_uring backend. Also, interaction bugs where a compiler frontend triggers a bug in LLVM aren’t uncommon since Rust was the first major frontend other than clang to exercise code paths. Indeed the (your?) fix in LLVM for this issue mentions Rust is impacted too.
I agree with the higher level points about stability and I don’t like Zig not being a safe language in this day and age, but I think your criticism about quality is a bit harsh if your source of this complaint is that they haven’t put a workaround for an LLVM bug.
Zig is one of my favorite new languages, I really like the cross-compiler too. I'm not a regular user yet but I'm hopeful for its long-term success as a language and ecosystem. It's still early days, beta/dev level instability is expected, and even fundamental changes in design. I think community input and feedback can be particularly valuable at this stage.
Maybe you would, but >95% of serious projects wouldn't. The typical lifetime of a codebase intended for a lasting application is over 15 or 20 years (in industrial control or aerospace, where low-level languages are commonly used, codebases typically last for over 30 years), and while such changes are manageable early on, they become less so over time.
You say "strict" as if it were out of some kind of stubborn princple, where in fact backward compatibility is one of the things people who write "serious" software want most. Backward compatibility is so popular that at some point it's hard to find any feature that is in high-enough demand to justify breaking it. Even in established languages there's always a group of people who want somethng badly enough they don't mind breaking compatibility for it, but they're almost always a rather small minority. Furthermore, a good record of preserving compatibility in the past makes a language more attractive even for greenfield projects written by people who care about backward compatibility, who, in "serious" software, make up the majority. When you pick a language for such a project, the expectation of how the language will evolve over the next 20 years is a major concern on day one (a startup might not care, but most such software is not written by startups).
Personally, it is a huge pain to rewrite things and update dependencies because the code I am depending on is moving out from under me. I also found this to be a big problem in Rust.
And another huge upside is you have access to best of everything. As an example, I am heavily using fuzz testing and I can very easily use honggfuzz which is the best fuzzer according to all research I could find, and also according to my experience so far.
From this perspective, it doesn’t make sense to use zig over c for professional work. If I am writing a lot of code then I don’t want to rewrite it. If am writing a very small amount of code with no dependencies, then it doesn’t matter what I use and this is the only case where I think zig might make sense.
That's a very good point, actually. However...
> with fewer footguns
..the Crab People[0] would definitely quibble with that particular claim of yours.
[0] https://en.wikipedia.org/wiki/Crab_People of course.
The entire C, C ABI and standard lib specs, combined, are probably less words than the Promise spec from ECMAScript 262.
A small language that stays consistent and predictable lets developers evolve it in best practices, patterns, design choices, tooling. C has achieved all that.
No evolving language has anywhere near that freedom.
I don't want an ever evolving Zig too for what is worth. And I like Zig.
I don't think any developer can resolve all of the design tensions a programming language has, you can't make it ergonomic on its own.
But a small, modern, stable C would still be welcome, besides Odin.
Yes, with almost complete lack of documentation and learning materials it is definitely the easiest language to learn.
Personally I'm glad that there are more people trying to break out of the C tar pit. Even if I'd never chose to use the language.
So what is your point?
Lol, I’ll borrow this.
It is such a readable language that I found it easier learning the API than most languages.
Zig has this on its side. Reading the unit tests directly from the code give, most of the time, a good example too.
https://tomas-svojanovsky.medium.com/mitchell-hashimoto-go-a...
https://www.youtube.com/watch?v=dJ5-41u-e7k
https://weeklyrust.substack.com/p/why-roc-is-moving-away-fro...
Perhaps there is room for both... via C FFI interop, of course, lol
(C FFI will probably long outlast C itself...)
Rust will also never replace C or C++ in any meaningful way, at best new code gets written in new languages (and Rust being only one among many, and among languages used for new projects will also be C and C++, just maybe not that often).
I think the era of 'pop star languages' is over, the programming language future is highly diverse (and that's a good thing).
Given that Rust is quite an old language now and its adoption is still so low, I don't think that should be much of a worry, although that doesn't mean Zig will be the option of choice, and not stabilising is certainly not a good sign. At Rust's adoption rate, a language that hasn't been invented yet and that would show a more normal rate of adoption for a popular language could easily overtake it.
> There is also the issue of will people actually code by then.
Now that could be a bigger issue. :)
Maybe it's just C++ teams being conservative, but a lot of them really seem to hate Rust when you talk with them for whatever reason. I can't imagine why though, but then I've only ever worked with C when I had to, and I have never worked with C++. From having played around with both C++ and Rust, I'd personally pick Rust, but I'm sure it's because I don't know enough. Either way I doubt I'll ever see Rust in a real world job in my corner of the world.
The other tailwind for Zig is that it’s easier than ever to translate an existing codebase with tests into a new language with AI.
Zig OTOH is clearly, to me at least (opinion alert), a "better C". It even compiles C!
I expect LLMs to be really good at converting C to Zig.
> There is also the issue of will people actually code by then.
LLMs don't take responsibility. So even if code is generated, a human will have to assess it. I think assessing Zig is easier than assessing C, which gives this language a selling point that holds out in the AI assisted programming future.
That requires literally rethinking every language and standard library facility and asking "is this putting up artificial roadblocks or even invoking straight UB when one tries to use it idiomatically in unsafe contexts?", then coming up with more flexible, more "C like" facilities in that case. It's hard work but quite doable.
Real example: I had to wait some seconds to compile and run benchmarks for a library and it re-compiles instantly (<100ms) with c.
Zig does have a single compilation unit and that might have some advantages but in practice it is a hard disadvantage. And I didn’t ever see someone pointing this out online.
I would really recommend trying to learn c with modernC book and try to do it with c for people like me building something from scratch
I remember when learning Zig, the documentation for the language itself was extensive, complete, and easily greppable due to being all on one page.
The standard library was a lot less intuitive, but I suspect that has more to do with the amount of churn it's still going through.
The build system also needs more extensive documentation in the same way that the stdlib does, but it has a guide that got me reasonably far with what came out of the box.
Is there any reason to be rushing it? Zig isn't languishing without activity. Big things are happening, and it's better in my opinion for them to get the big important stuff right early than it is to get something stable that is harder to change and improve later.
"Competing with C" means innovating, not striving to meet feature parity so it can be frozen in time. It's not as though C has anything terribly exciting going on with it. Let them cook.
And if the new software stack just improves a fraction of the ecosystem, it isn't worth the effort.
Those are the companies I care about when chosing which programing languages to invest my time on.
Either those applications are actively maintained, or they aren't. Part of the active maintenance is to decide whether to upgrade to a new compiler toolchain version (e.g. when in doubt, "never change a running system"), old compiler toolchains won't suddenly stop working.
FWIW, trying to build a 20 or 30 year old C or C++ application in a modern compiler also isn't exactly trivial, depending on the complexity of the code base (especially when there's UB lurking in the code, or the code depends on specific compiler bugs to be present - e.g. changing anything in a project setup always comes with risks attached).
I really see no advantage for Zig over Rust after you get past that 2 first two weeks.
But I digress. I was thinking of Zig in comparison to C when I wrote that. I don't have a problem conceding that point, but I still believe the overall argument is correct to point to Zig specifically in the case of writing code to optimize a hotpath behind FFI; it is much easier to get to more optimal code and cross-compilation is easier to boot (i.e. to support Darwin/AppleSilicon for dev laptops, and both Linux/x64 and Linux/arm64 for cloud servers).
Hardly anything radical.
Both undoubtedly are talented programmers, but you overestimate impact and importance of these project.
GitHub stars and HN posts are not very good indicator of what happens in the real world
Not only do I disagree it never will, I think it's already well on its way to doing exactly that.
This is ironic since these two crowds are mostly solving the same type of problems. It's just democrats vs republicans type of split, some of it is just for show and philosophical.
So being part of 3 major OS (Windows, Android and now Linux), the big 3 cloud providers having SDKs for the language, used by so much tooling (js + python) and being part of major internet infrastructure means its “slow” adoption then wow…
The same can be said about Zig's comptime. It's entirely unlike anything C, C++ or Rust has to offer.
> I expect LLMs to be really good at converting C to Zig.
While it's possible to translate C to Zig code - and you don't need an LLM for that, it's a Zig compiler/build-system feature - the result will be quite different from a project that's developed in Zig from the ground up since the translation output wouldn't make use of Zig's unique features (and Zig isn't really unique as 'C translation target', C can also be translated to unsafe Rust, or even to Javascript - see early Emscripten versions).
Also, the 'C compatibility' of Zig is implemented via a separate compiler frontend, Rust toolchains could do exactly the same thing by integrating the Clang frontend into the Rust compiler.
Or should I say, I've not written a single line of Zig because I've been managing AI's coding in Zig.
Turns out Zig is a fantastic language to "centaur" on. (Reference is to "centaur chess" and which is also sort of becoming a term indicating close code design cooperation with an LLM.)
All of that C code that the LLM trained on ends up helping it work out algorithms in Zig, except that Zig has waaaay more safety guarantees and checks. And is often faster compiled code than the equivalent C, which is astonishing.
In other words, the Rust approach to safety is to make as few unsafe LoC as possible, and the Zig approach is to make every unsafe line as safe as possible. As long as their philosophical approach to safety is such that Zig makes writing unsafe code easy and Rust avoids it as much as possible, then writing unsafe code in Zig will always be easier.
It’s not like Clojure or Common Lisp, where a decades old software still runs, mostly unmodified, the same today, any changes mainly being code written for a different environment or even compiler implementation. This is largely because they take breaking user code way more seriously. Alot of code written in these languages seem to have similar timelessness too. Software can be “done”.
Of course, but you want to make that as easy as you can. Compatibility is never binary (which is why I hate semantic versioning), but you should strive for the greatest compatibility for the greatest portion of users.
> FWIW, trying to build a 20 or 30 year old C or C++ application in a modern compiler also isn't exactly trivial
I know that well (especially for C++; in C the situation is somewhat different), and the backward compatibility of C++ compilers leaves much to be desired.
Rust has definitely gained some ground while they're hardly any relevant products using Zig.
A language doing it today is doing it in the context of an ecosystem where even higher level languages exist and they have made the choice to target a lower level of abstraction.
In theory no. In practice it really does.
> unsafe code is still unsafe
Ok, but most rust code is not unsafe while all zig code is unsafe.
> and the borrow checker and Rust's language complexity are their own kind of footguns
Please elaborate. They are something to learn but I don’t see the footgun. A footgun is a surprisingly defect that’s pointed at your foot and easy to trigger (ie doing something wrong and your foot blows off). I can’t think how the borrow checker causes that when it’s the exact opposite - you can’t ever create a footgun without doing unsafe because it won’t even compile.
> but I still believe the overall argument is correct to point to Zig specifically in the case of writing code to optimize a hotpath behind FFI; it is much easier to get to more optimal code and cross-compilation is easier to boot (i.e. to support Darwin/AppleSilicon for dev laptops, and both Linux/x64 and Linux/arm64 for cloud servers).
I agree cross compilation with zig is significantly easier but Rust isn’t that hard, especially with the cross-rs crate making it significantly simpler. Performance, Rust is going to be better - zig makes you choose between safety and performance and even in unsafe mode there’s various things that cause better codegen. For example zig follows the C path of manual noalias annotations which has been proven to be non scalable and difficult to make operational. Rust does this for all variables automatically because it’s not allowed in the language.
Zig is trying to get me instant compilation and I see that as a huge advantage for Zig (even past the first 2 weeks).
I'll probably stick with Rust as my "low level language" due to its safety, type system, maturity, library ecosystem, and career opportunities.
But I remain jealous of Zig's willingness to do extreme things to make compilation faster.
This page contains a curated list of recent changes to main branch Zig.
This page contains entries for the year 2026. Other years are available in the Devlog archive page.
February 13, 2026
Author: Andrew Kelley
As we approach the end of the 0.16.0 release cycle, Jacob has been hard at work, bringing std.Io.Evented up to speed with all the latest API changes:
Both of these are based on userspace stack switching, sometimes called “fibers”, “stackful coroutines”, or “green threads”.
They are now available to tinker with, by constructing one’s application using std.Io.Evented. They should be considered experimental because there is important followup work to be done before they can be used reliably and robustly:
IoMode.evented for the compilerWith those caveats in mind, it seems we are indeed reaching the Promised Land, where Zig code can have Io implementations effortlessly swapped out:
const std = @import("std");
pub fn main(init: std.process.Init.Minimal) !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
const gpa = debug_allocator.allocator();
var threaded: std.Io.Threaded = .init(gpa, .{
.argv0 = .init(init.args),
.environ = init.environ,
});
defer threaded.deinit();
const io = threaded.io();
return app(io);
}
fn app(io: std.Io) !void {
try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
}
$ strace ./hello_threaded
execve("./hello_threaded", ["./hello_threaded"], 0x7ffc1da88b20 /* 98 vars */) = 0
mmap(NULL, 262207, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f583f338000
arch_prctl(ARCH_SET_FS, 0x7f583f378018) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
prlimit64(0, RLIMIT_STACK, {rlim_cur=16384*1024, rlim_max=RLIM64_INFINITY}, NULL) = 0
sigaltstack({ss_sp=0x7f583f338000, ss_flags=0, ss_size=262144}, NULL) = 0
sched_getaffinity(0, 128, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31]) = 8
rt_sigaction(SIGIO, {sa_handler=0x1019d90, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=0x1019d90, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
writev(1, [{iov_base="Hello, World!\n", iov_len=14}], 1Hello, World!
) = 14
rt_sigaction(SIGIO, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, NULL, 8) = 0
exit_group(0) = ?
+++ exited with 0 +++
Swapping out only the I/O implementation:
const std = @import("std");
pub fn main(init: std.process.Init.Minimal) !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
const gpa = debug_allocator.allocator();
var evented: std.Io.Evented = undefined;
try evented.init(gpa, .{
.argv0 = .init(init.args),
.environ = init.environ,
.backing_allocator_needs_mutex = false,
});
defer evented.deinit();
const io = evented.io();
return app(io);
}
fn app(io: std.Io) !void {
try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
}
execve("./hello_evented", ["./hello_evented"], 0x7fff368894f0 /* 98 vars */) = 0
mmap(NULL, 262215, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70a4c28000
arch_prctl(ARCH_SET_FS, 0x7f70a4c68020) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
prlimit64(0, RLIMIT_STACK, {rlim_cur=16384*1024, rlim_max=RLIM64_INFINITY}, NULL) = 0
sigaltstack({ss_sp=0x7f70a4c28008, ss_flags=0, ss_size=262144}, NULL) = 0
sched_getaffinity(0, 128, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31]) = 8
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70a4c27000
mmap(0x7f70a4c28000, 548864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70a4ba1000
io_uring_setup(64, {flags=IORING_SETUP_COOP_TASKRUN|IORING_SETUP_SINGLE_ISSUER, sq_thread_cpu=0, sq_thread_idle=1000, sq_entries=64, cq_entries=128, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS|IORING_FEAT_CQE_SKIP|IORING_FEAT_LINKED_FILE|IORING_FEAT_REG_REG_RING|IORING_FEAT_RECVSEND_BUNDLE|IORING_FEAT_MIN_TIMEOUT|IORING_FEAT_RW_ATTR|IORING_FEAT_NO_IOWAIT, sq_off={head=0, tail=4, ring_mask=16, ring_entries=24, flags=36, dropped=32, array=2112, user_addr=0}, cq_off={head=8, tail=12, ring_mask=20, ring_entries=28, overflow=44, cqes=64, flags=40, user_addr=0}}) = 3
mmap(NULL, 2368, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, 3, 0) = 0x7f70a4ba0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, 3, 0x10000000) = 0x7f70a4b9f000
io_uring_enter(3, 1, 1, IORING_ENTER_GETEVENTS, NULL, 8Hello, World!
) = 1
io_uring_enter(3, 1, 1, IORING_ENTER_GETEVENTS, NULL, 8) = 1
munmap(0x7f70a4b9f000, 4096) = 0
munmap(0x7f70a4ba0000, 2368) = 0
close(3) = 0
munmap(0x7f70a4ba1000, 548864) = 0
exit_group(0) = ?
+++ exited with 0 +++
Key point here being that the app function is identical between those two snippets.
Moving beyond Hello World, the Zig compiler itself works fine using std.Io.Evented, both with io_uring and with GCD, but as mentioned above, there is a not-yet-diagnosed performance degradation when doing so.
Happy hacking,
Andrew
February 06, 2026
Author: Andrew Kelley
If you have a Zig project with dependencies, two big changes just landed which I think you will be interested to learn about.
Fetched packages are now stored locally in the zig-pkg directory of the project root (next to your build.zig file).
For example here are a few results from awebo after running zig build:
$ du -sh zig-pkg/*
13M freetype-2.14.1-alzUkTyBqgBwke4Jsot997WYSpl207Ij9oO-2QOvGrOi
20K opus-0.0.2-vuF-cMAkAADVsm707MYCtPmqmRs0gzg84Sz0qGbb5E3w
4.3M pulseaudio-16.1.1-9-mk_62MZkNwBaFwiZ7ZVrYRIf_3dTqqJR5PbMRCJzSuLw
5.2M uucode-0.1.0-ZZjBPvtWUACf5dqD_f9I37VGFsN24436CuceC5pTJ25n
728K vaxis-0.5.1-BWNV_AxECQCj3p4Hcv4U3Yo1WMUJ7Z2FUj0UkpuJGxQQ
It is highly recommended to add this directory to the project-local source control ignore file (e.g. .gitignore). However, by being outside of .zig-cache, it provides the possibility of distributing self-contained source tarballs, which contain all dependencies and therefore can be used to build offline, or for archival purposes.
Meanwhile, an additional copy of the dependency is cached globally. After filtering out all the unused files based on the paths filter, the contents are recompressed:
$ du -sh ~/.cache/zig/p/*
2.4M freetype-2.14.1-alzUkTyBqgBwke4Jsot997WYSpl207Ij9oO-2QOvGrOi.tar.gz
4.0K opus-0.0.2-vuF-cMAkAADVsm707MYCtPmqmRs0gzg84Sz0qGbb5E3w.tar.gz
636K pulseaudio-16.1.1-9-mk_62MZkNwBaFwiZ7ZVrYRIf_3dTqqJR5PbMRCJzSuLw.tar.gz
880K uucode-0.1.0-ZZjBPvtWUACf5dqD_f9I37VGFsN24436CuceC5pTJ25n.tar.gz
120K vaxis-0.5.1-BWNV_BFECQBbXeTeFd48uTJRjD5a-KD6kPuKanzzVB01.tar.gz
The motivation for this change is to make it easier to tinker. Go ahead and edit those files, see what happens. Swap out your package directory with a git clone. Grep your dependencies all together. Configure your IDE to auto-complete based on the zig-pkg directory. Run baobab on your dependency tree. Furthermore, by having the global cache have compressed files instead makes it easier to share that cached data between computers. In the future, it is planned to support peer-to-peer torrenting of dependency trees. By recompressing packages into a canonical form, this will allow peers to share Zig packages with minimal bandwidth. I love this idea because it simultaneously provides resilience to network outages, as well as a popularity contest. Find out which open source packages are popular based on number of seeders!
The second change here is the addition of the --fork flag to zig build.
In retrospect, it seems so obvious, I don’t know why I didn’t think of it since the beginning. It looks like this:
zig build --fork=[path]
This is a project override option. Given a path to a source checkout of a project, all packages matching that project across the entire dependency tree will be overridden.
Thanks to the fact that package content hashes include name and fingerprint, this resolves before the package is potentially fetched.
This is an easy way to temporarily use one or more forks which are in entirely separate directories. You can iterate on your entire dependency tree until everything is working, while using comfortably the development environment and source control of the dependency projects.
The fact that it is a CLI flag makes it appropriately ephemeral. The moment you drop the flags, you’re back to using your pristine, fetched dependency tree.
If the project does not match, an error occurs, preventing confusion:
$ zig build --fork=/home/andy/dev/mime
error: fork /home/andy/dev/mime matched no mime packages
$
If the project does match, you get a reminder that you are using a fork, preventing confusion:
$ zig build --fork=/home/andy/dev/dvui
info: fork /home/andy/dev/dvui matched 1 (dvui) packages
...
This functionality is intended to enhance the workflow of dealing with ecosystem breakage. I already tried it a bit and found it to be quite pleasant to work with. The new workflow goes like this:
--fork until your project works again. During this time you can use the actual upstream source control, test suite, zig build test --watch -fincremental, etc.…and you can probably skip the step where you switch your build.zig.zon to your fork unless you expect upstream to take a long time to merge your fixes.
February 03, 2026
Author: Andrew Kelley
The Windows operating system provides a large ABI surface area for doing things in the kernel. However, not all ABIs are created equally. As Casey Muratori points out in his lecture, The Only Unbreakable Law, the organizational structure of software development teams has a direct impact on the structure of the software they produce.
The DLLs on Windows are organized into a heirarchy, with some of the APIs being high-level wrappers around lower-level ones. For example, whenever you call functions of kernel32.dll, ultimately, the actual work is done by ntdll.dll. You can observe this directly by using ProcMon.exe and examining stack traces.
What we’ve learned empirically is that the ntdll APIs are generally well-engineered, reasonable, and powerful, but the kernel32 wrappers introduce unnecessary heap allocations, additional failure modes, unintentional CPU usage, and bloat.
This is why the Zig standard library policy is to Prefer the Native API over Win32. We’re not quite there yet - we have plenty of calls into kernel32 remaining - but we’ve taken great strides recently. I’ll give you two examples.
According to the official documentation, Windows does not have a straightforward way to get random bytes.
Many projects including Chromium, boringssl, Firefox, and Rust call SystemFunction036 from advapi32.dll because it worked on versions older than Windows 8.
Unfortunately, starting with Windows 8, the first time you call this function, it dynamically loads bcryptprimitives.dll and calls ProcessPrng. If loading the DLL fails (for example due to an overloaded system, which we have observed on Zig CI several times), it returns error 38 (from a function that has void return type and is documented to never fail).
The first thing ProcessPrng does is heap allocate a small, constant number of bytes. If this fails it returns NO_MEMORY in a BOOL (documented behavior is to never fail, and always return TRUE).
bcryptprimitives.dll apparently also runs a test suite every time you load it.
All that ProcessPrng is really doing is NtOpenFile on "\\Device\\CNG" and reading 48 bytes with NtDeviceIoControlFile to get a seed, and then initializing a per-CPU AES-based CSPRNG.
So the dependency on bcryptprimitives.dll and advapi32.dll can both be avoided, and the nondeterministic failure and latencies on first RNG read can also be avoided.
ReadFile looks like this:
pub extern "kernel32" fn ReadFile(
hFile: HANDLE,
lpBuffer: LPVOID,
nNumberOfBytesToRead: DWORD,
lpNumberOfBytesRead: ?*DWORD,
lpOverlapped: ?*OVERLAPPED,
) callconv(.winapi) BOOL;
NtReadFile looks like this:
pub extern "ntdll" fn NtReadFile(
FileHandle: HANDLE,
Event: ?HANDLE,
ApcRoutine: ?*const IO_APC_ROUTINE,
ApcContext: ?*anyopaque,
IoStatusBlock: *IO_STATUS_BLOCK,
Buffer: *anyopaque,
Length: ULONG,
ByteOffset: ?*const LARGE_INTEGER,
Key: ?*const ULONG,
) callconv(.winapi) NTSTATUS;
As a reminder, the above function is implemented by calling the below function.
Already we can see some nice things about using the lower level API. For instance, the real API simply gives us the error code as the return value, while the kernel32 wrapper hides the status code somewhere, returns a BOOL and then requires you to call GetLastError to find out what went wrong. Imagine! Returning a value from a function 🌈
Furthermore, OVERLAPPED is a fake type. The Windows kernel doesn’t actually know or care about it at all! The actual primitives here are events, APCs, and IO_STATUS_BLOCK.
If you have a synchronous file handle, then Event and ApcRoutine must be null. You get the answer in the IO_STATUS_BLOCK immediately. If you pass an APC routine here then some old bitrotted 32-bit code runs and you get garbage results.
On the other hand if you have an asynchronous file handle, then you need to either use an Event or an ApcRoutine. kernel32.dll uses events, which means that it’s doing extra, unnecessary resource allocation and management just to read from a file. Instead, Zig now passes an APC routine and then calls NtDelayExecution. This integrates seamlessly with cancelation, making it possible to cancel tasks while they perform file I/O, regardless of whether the file was opened in synchronous mode or asynchronous mode.
For a deeper dive into this topic, please refer to this issue:
Windows: Prefer the Native API over Win32
January 31, 2026
Author: Andrew Kelley
Over the past month or so, several enterprising contributors have taken an interest in the zig libc subproject. The idea here is to incrementally delete redundant code, by providing libc functions as Zig standard library wrappers rather than as vendored C source files. In many cases, these functions are one-to-one mappings, such as memcpy or atan2, or trivially wrap a generic function, like strnlen:
fn strnlen(str: [*:0]const c_char, max: usize) callconv(.c) usize {
return std.mem.findScalar(u8, @ptrCast(str[0..max]), 0) orelse max;
}
So far, roughly 250 C source files have been deleted from the Zig repository, with 2032 remaining.
With each function that makes the transition, Zig gains independence from third party projects and from the C programming language, compilation speed improves, Zig’s installation size is simplified and reduced, and user applications which statically link libc enjoy reduced binary size.
Additionally, a recent enhancement now makes zig libc share the Zig Compilation Unit with other Zig code rather than being a separate static archive, linked together later. This is one of the advantages of Zig having an integrated compiler and linker. When the exported libc functions share the ZCU, redundant code is eliminated because functions can be optimized together. It’s kind of like enabling LTO (Link-Time Optimization) across the libc boundary, except it’s done properly in the frontend instead of too late, in the linker.
Furthermore, when this work is combined with the recent std.Io changes, there is potential for users to seamlessly control how libc performs I/O - for example forcing all calls to read and write to participate in an io_uring event loop, even though that code was not written with such use case in mind. Or, resource leak detection could be enabled for third-party C code. For now this is only a vaporware idea which has not been experimented with, but the idea intrigues me.
Big thanks to Szabolcs Nagy for libc-test. This project has been a huge help in making sure that we don’t regress any math functions.
As a reminder to our users, now that Zig is transitioning to being the static libc provider, if you encounter issues with the musl, mingw-w64, or wasi-libc libc functionality provided by Zig, please file bug reports in Zig first so we don’t annoy maintainers for bugs that are in Zig, and no longer vendored by independent libc implementation projects.
The very same day I sat at home writing this devlog like a coward, less than five miles away, armed forces who are in my city against the will of our elected officials shot tear gas, unprovoked, at peaceful protestors. Next time I hope to have the courage to join my neighbors, and I hope to not get shot like Alex Pretti and Renée Good.
C++ on the other hand? Possibly, though I think that it's just as much because of the own-goals of the C++ standards committee as it is the successes of Rust. I don't really consider Zig a competitor in this space because if you're reaching for C++, you are reaching for a level of abstraction that Zig is unwilling to provide.
This is a painfully shallow framing.
Yes, programming languages solve problems by emitting instructions that a programmable logic chip can use to preform calculations on input resulting in output. And the scaffolding you use to get there isn't just a matter of philosophical show. Rust as a first order decision will refuse to emit perfectly valid programs because it's unable to prove it's correctness. Zig will emit any program it has enough information to do so. People coding in rust off load much of the effort in understanding and proving that correctness to the compiler. In Zig that relationship is reversed, where the compiler offloads that responsibility to the programmer.
The person you responded to is correct. For some people. Rust solves the difficult and annoying problems, for others it creates difficult and annoying problems.
Some people like creating art, some people like creating software. I guess you could frame that as philosophical, but to call it a political show, belies ignorance to the interactions between systems and predispositions of individuals.
> If anything, the Rust language would benefit from adding as much friction to unsafe code as possible
The friction is there already. I'm not advocating for getting rid of explicit 'unsafe' markings, 'SAFETY' comments or even clunky boilerplate, just for closing a very real gap in capability.
A full build was definitely much faster, but not as useful. Especially when using a build system with shared networked caching (Bazel for example).
Yes those projects were a bloated mess, as it always seems to be.
Interestingly, Carbon is kinda trying to tackle both at the same time (though starting from C++ in their case) which is a bit of a challenge.
It's true that the total market share for low level languages (C + C++ + Rust + Zig + others) continues declining, as it has for a couple of decades now (that may change if coding agents start writing everything in C [1] but it's not happening yet), but that's all the more reason to find some "safe bet" within that diminishing total market. Rust's modest success is enough for some, but clearly not enough for many others to be seen as a safe bet.
There's no denying Rust's popularity in open-source CLI dev tools for Python and JS/TS, but when you talk to C/C++ shops who've evaluated Rust and see how many of them end up using it (and to what extent) you see it's not like it's been with languages that ended up achieving real popularity (which includes not only super-popular languages like C, C++, and Java, but also mid-popular languages like Go).
Specially because it's not a drop-in replacement for C++. As Zig is for C.
So when Zig hits 1.0 these companies will probably consider Zig much more than they do today. Understandably.
OTOH going from C++ (OO) to Rust (not OO, borrow checker) is a big leap.
This has a big effect on unsafe code. When unsafe code gets called indirectly from safe code, the unsafe code has to make sure that whatever the safe code does, the result is still safe. This requires very careful design of the interface provided by the unsafe code.
I think it is a research question whether Zig would make writing Rust unsafe code a lot easier. In particular the boundary between safe Rust and unsafe code written in Zig could become very tricky. Possibly tricky enough that it would be as complex to write as unsafe Rust today.
No, not even remotely. ABI-stability in C++ means that C++ is stuck with suboptimal implementations of stdlib functions, whereas Rust only stabilizes the exposed interface without stabilizing implementation details.
> Unfortunately editions don't allow breaking changes in the standard library
Surprisingly, this isn't true in practice either. The only thing that Rust needs to guarantee here is that once a specific symbol is exported from the stdlib, that symbol needs to be exported forever. But this still gives an immense amount of flexibility. For example, a new edition could "remove" a deprecated function by completely disallowing any use of a given symbol, while still allowing code on an older edition to access that symbol. Likewise, it's possible to "swap out" a deprecated item for a new item by atomically moving the deprecated item to a new namespace and making the existing item an alias to that new location, then in the new edition you can change the alias to point to the new item instead while leaving the old item accessible (people are exploring this possibility for making non-poisoning mutexes the default in the next edition).
ISO C++ standard is silent on how the ABI actually looks like, the ABI not being broken in most C and C++ compilers is a consequence of customers of those compilers not being happy about breakages.
It has issues like panicking or segfaulting when using some data types (arrow array types) in the wrong place.
It is extremely difficult to write an arrow implementation in Rust.
It is much easier to do it in zig or c(without strict aliasing).
I also had the same experience with glommio in Rust.
Also the binary that we produce compiles in several minutes and is above 30mb. This is an insane amount of bloat. And unfortunately I don’t think there is another feasible way of doing this kind of work in rust, because it is so hard to write proper low level code.
I don’t agree with noalias being bad personally. I fuond it is the only way to do it. It is much harder to write code with pointers with implicit aliasing like c has by default and rust has as the only option. And you don’t ever need to use noalias except some rare places.
To make it clear, I mean the huge footgun in rust is producing a ton of bloat and subpar code because you can’t write much and you end up depending on too many libraries
Close, but not the way I think of a footgun. A footgun is code that was written in a naive way, looks correct, submitted, and you find out after submitting it that it was erroneous. Good design makes it easy for people to do the right thing and difficult to do the wrong thing.
In Rust it is extremely easy to hit the borrow checker including for code which is otherwise safe and which you know is safe. You walk on eggshells around the borrow checker hoping that it won't fire and shoot you in the foot and force you to rewrite. It is not a runtime footgun, it is a devtime footgun.
Which, to be fair, is sometimes desired. When you have a 1m+ LOC codebase and dozens of junior engineers working on it and requirements for memory safety and low latency requirements. Fair enough trade-off in that case.
But in Zig, you can just call defer on a deinit function. Complexity is the eternal enemy, and this is just a much simpler approach. The price of that simplicity is that you need to behave like an adult, which if the codebase (hotpath optimization) is <1k LOC I think is eminently reasonable.
GitHub's survey did not say much about Rust I think, despite Rust projects often having lots of starring. Rust projects might have a greater ratio of stars-to-popularity than projects in other languages, though.
StackOverflow's survey was much more optimistic or indicated popularity for Rust.
Redmonk places Rust at place 19th.
Secondly, let us know when Amazon rewrites Firecracker in Zig, Android replaces Rust with Zig, or Mark Russinovich goes to some Zig conference explaining why Azure is dropping Rust for Zig,
"Mark Russinovich, Microsoft Azure CTO tells Rust Nation UK 2025 why Azure is moving to Rust from C++"
C++ came out in 1985 and competed with C, COBOL, Pascal and FORTRAN. It was an overall improvement than those and therefore there is a legit reason for it to take off.
> how many of them end up using it (and to what extent) you see it's not like it's been with languages that ended up achieving real popularity
I assume many places that have a huge codebase in C++ would just do a port to Rust. That would almost always cause problems but for greenfield projects it's a no brainer IMO.
So even though C++ is the language I reach for outside Java, C#, TypeScript, I would assert that downplaying Rust adoption by Amazon, Adobe, Microsoft, Google, is losing track where things are going.
No, this completely overestimates how quickly languages gain prominence.
C came out in 1972 and didn't gain its current dominance until approximately the release of the ANSI C spec in 1989/1990, after 17 years.
C++ came out in 1985 and didn't become the dominant language for gamedev until the late 90s (after it had its business-language-logic niche completely eaten by Java), after 14 years or so.
Python came out in 1991 and labored as an obscure Perl alternative until the mid-late 2000s, after about 16 years (we can carbon-date the moment of its popularity by looking at when https://xkcd.com/353/ was released).
Javascript came out in 1995 and was treated as a joke and/or afterthought in the broader programming discourse until Node.js came out in 2009, after 14 years.
Rust is currently 11 years old, and it's doing quite excellently for its age.
For instance, any &mut reference in Rust is assumed not to be aliased, and any &reference not involving UnsafeCell<...> is assumed not to be written to. These implied contracts can be loosened, e.g. by using &Cell<> if applicable (may alias, can be read or written safely, but only "as a whole object": access to the internals does not escape beyond any single operation) which is arguably closer to idiomatic C.
MaybeUninit<> is another common example: C and Zig code often works with possibly-uninitialized data, but this possibility has to be accounted for explicitly in a safe Rust interface. It's always insta-UB if a safe Rust function is passed uninitialized data, even when the equivalent would work idiomatically in C.
Nothing is forcing you to do that other than it’s easy to add dependencies. I don’t see how zig is much different
One business domain that Rust currently doesn't have an answer for, is selling commercial SDKs with binary libraries, which is exactly the kind of customers that get pissed off when C and C++ compilers break ABIs.
Microsoft mentions this in the adoption issues they are having with Rust, see talks from Victor Ciura, and while they can work around this with DLLs and COM/WinRT, it isn't optimal, after all Rust's safety gets reduced to the OS ABI for DLLs and COM.
In theory. In practice the standards committee, consisting of compiler vendors and some of their users, shape the standard, and thus the standard just so happens to conspire to avoid ABI breakages.
This is in part why Google bowed out of C++ standardization years ago.
You’re contradicting yourself a bit here I think. Erroneous code generally won’t compile whereas in Zig it will happily do so. Also, Zig has plenty of foot guns (eg forgetting to call defer on a deinit but even misusing noalias or having an out of bounds result in memory corruption). IMHO the zig footgun story with respect to UB behavior is largely unchanged relative to C/C++. It’s mildly better but it’s closer to C/C++ than being a safe language and UB is a huge ass footgun in any moderate complexity codebase.
https://www.devjobsscanner.com/blog/top-8-most-demanded-prog...
https://uk.indeed.com/career-advice/career-development/codin...
https://www.itransition.com/developers/in-demand-programming...
https://www.hackerrank.com/blog/top-developer-skills-in-2025...
Viewing these numbers through an optimistic or pessimistic lens is a matter of perspective and, of course, no one knows the future. But when you compare Rust, which is a middle-aged language now, to how languages that ended up "making it" were at the same age, the comparison is not favourable.
Of course. The rate of adoption is related to the increase in value compared to the status quo, much like how genes spread. But Rust's adoption is slow precisely because its "fitness benefit" is low.
> That would almost always cause problems but for greenfield projects it's a no brainer IMO.
It would have been a no brainer if, when writing a new codebase expected to last 20 years or more (which is often the case with software written in low-level languages), you'd believe the chosen language to be very popular over the next few deacdes. But given its slow adoption compared to languages that ended up achieving that status, despite it's rather old age, it's not looking like a safe bet, which is why Rust's adoption for important greenfield projects is also low (again, relative to other languages).
Its Zig 0.15 effort started in August and was only complete in October (see first PR at https://github.com/ghostty-org/ghostty/pull/8372). And many issues were encountered and solved along the way. And of course during all of this they also encountered an issue in Zig itself: https://github.com/ziglang/zig/issues/24627
> that when the time gets to be rewritten it won't surely be C++.
It looks like it won't be Rust, either. I mean, some may be written in Rust, but not the majority. My point is just that as much as some erstwhile Haskell fans have taken to Rust, comparing Rust's adoption to Haskell's - a language whose joke motto was "avoid success at all costs" - is not the right metric. Given that Rust's goal was to replace C++, its success should be compared to C++ and other languages that ended up achieving similar success. I'm saying that compared to them Rust's success has been mediocre, if that, and it's not a young language anymore by any stretch of the imagination.
Though I can imagine that unsafe Rust still has to many of the safe Rust's rules. So there could be a better unsafe language that has fewer restrictions.
The only major UB from C that zig doesn’t address is use after free afaik. How is that largely unchanged???
Just having an actual strong type system w/o the “billion dollar mistake” is a large change.
> Carbon: graduating from the experiment
https://ndctoronto.com/agenda/carbon-graduating-from-the-exp...
As for it being widely adopted, people keeping missing the point that Carbon is mostly for Google themselves, as means to integrate into existing C++ projects.
They are the very first ones to assert that for green field projects there are already plenty of safe languages to chose from.
So many language designers would dream to have such adoption numbers by tech giants for their hobby language.
Thus it will never be even considered outside the tech bubble.
Do you know one industry that likes very much tossing closed-source proprietary blobs over the wall?
Game studios, and everyone that works in the games industry providing tooling for AAA studios.
* Double free
* Out of bounds array access
* Dereferencing null pointers
* Misaligned pointer dereference
* Accessing uninitialized memory
* Signed integer overflow
* Accessing a union field for which the active tag is something else.
In case that you are well familiar with for instance pattern matching, might you have any opinions on the pattern matching that is currently proposed for Carbon?
https://docs.carbon-lang.dev/docs/design/pattern_matching.ht...
You know what else is common in the games industry? C# and NDA's.
C# means that game development is no longer a C/C++ monoculture, and if someone can make their engine or middleware usable with C# through an API shim, Native AOT, or some other integration, there are similar paths forward for using Rust, Zig, or whatever else.
NDA's means that making source available isn't as much of a concern. Quite a bit of the modern game development stack is actually source-available, especially when you're talking about game engines.
https://github.com/ityonemo/clr
(Btw: you can't null pointer dereference in zig without using the navigation operator which will panic on null; you can't misalign a pointer unless you use @alignCast which will also create a panic)
Regarding the linked pattern matching proposal, it seems alright to me, not everything has to be ML like.
If you believe I mischaracterized zig, please enlighten me what I got wrong specifically rather than attacking my ad hominem
match (0, 1, 2) {
case (F(), 0, G()) => ...
}
> Here (F(), 0, G()) is not an expression, but three separate expressions in a tuple pattern. As a result, this code will call F() but not G(), because the mismatch between the middle tuple elements will cause pattern matching to fail before reaching G(). Other than this short-circuiting behavior, a tuple pattern of expression patterns behaves the same as if it were a single expression pattern.How would that work with exhaustiveness checking? As far as I can tell, they themselves believe that Carbon's exhaustiveness checking will be very poor.
And OK with implicit conversions? Especially when combined with their way of handling templates for pattern matching?
Arguing about whether certain static analysis should be opt in or opt out is just extremely uninteresting. It’s not like folks are auditing the unsafe blocks in their dependencies anyways.
If you want to talk about actual type system issues that’s more interesting.