Why over 2 years?
Like VC investment, Id assume a lump sum up front allow them to move faster with that money (hiring the right people sooner, etc.)
I wonder if projects like this care more about predictability of income (e.g. not hiring people depending on future funding to sustain them)
Anyway, it should be widespread, since it's basically true, though perhaps more relevant for physical skills than for donation scheduling.
> Id assume a lump sum up front allow them to move faster with that money (hiring the right people sooner, etc.)
On the other hand, the monthly payments mean they’re less likely to overcommit their spending up front.
If they’re hiring someone with twice-monthly paychecks, receiving the money up front doesn’t make much difference unless they want to hire based on projections of higher future donations, which is a risky move.
In fact, working on this client prompted me to start working on another Zig project, asynchronous I/O framework, which I'll be integrating with the NATS client soon: https://github.com/lalinsky/zio
2¹⁹ bytes, or 512KiB.
I may have been looking at the binary year 2038 countdown :D https://retr0.id/stuff/2038/
Zig has changed my life, and our team, by making TigerBeetle possible. It's been an incredible journey these past 5 years, and excited that we can now pay it back (and forward!) to the Zig Software Foundation, also matching with my friend Derek Collison and Synadia in doing so.
Thanks to Andrew for creating something beautifully special for the world in Zig, and to all the Zig core team and communities.
If you don't yet donate to the foundation, please consider doing so: https://ziglang.org/zsf/
Note that io_uring doesn’t require a pre-allocated core for spinning. There are multiple modes. We just use the kernel thread pool, which we prefer to a user space thread pool!
To be clear, we do invest in months of onboarding in terms of understanding the TigerBeetle code base. For example, matklad has recorded nearly a hundred hours' worth of IronBeetle episodes [0].
But I just noticed at one point that people were joining our team and never having any trouble with Zig. The question was just never being asked. Essential simplicity is a great benefit!
[0] https://www.youtube.com/playlist?list=PL9eL-xg48OM3pnVqFSRyB...
(though, to be fair, my Rust experience was a great help for learning Zig, just as my C++ knowledge was instrumental in grokking Rust)
The license model always made me uncomfortable for when you were using the commercial compilers though. Does this lock you into Spark forever?
What do people find more enjoyable?
>> Why is that?
> Zig is more enjoyable than Rust
You didn't really leave the GP more informed than before.
https://doc.rust-lang.org/reference/
vs
Tastes are just subjective.
Sometimes I just want to try out stuff, I know there is a bug, in fact, it breaks everything, but that's exactly what I want, I will revert it once my little expertement is complete. Strict languages will refuse to run anything unless you make sure that code follow all the rules, which is useless work as you know it won't stay (because it breaks things on purpose).
It is not just Rust, it is just that Rust has a lot of that. There is also C++ not allowing me to access private class members, or Python being annoying with its indentation, these are all good things for production code, but it gets in the way of experimentation.
Combine this with slow compilation times and it makes Rust not very experiment-friendly, which I think is the reason why many people don't have a good time with it.
As I understand it, Zig is more permissive and also has fast compilation as a design goal, which I think is a big contributing factor in making it more "pleasant".
What I like the most is a middle ground: have a language that it permissive when it comes to generating code, but be strict with warnings. Again, problem is that too many people don't care about warnings. Way too many times, I had people call me to investigate their bugs, see a warning, point it out and tell them "here, that's your bug".
I know of _ways_ to do this, but has anyone done this successfully with a RAG+version locked docs or something like that?
There are some cases the borrow checker requires you to go through hoops for but I see that as a win for adding friction and raising visibility of weird patterns.
And yes, there are cases that can't be expressed the same way,
There's no Rabbi out there to mandate that your code is kosher, using unsafe is OK.
Also, the situation where you really need it are rare in practice (in 9 years of full time Rust, I've yet to encounter one).
I encourage you to write a doubly linked list in Rust if you want to understand what I mean about "un-kosher" code. This is a basic academic example, but Rust itself is the rabbi that makes your life suck if you stray off the narrow path.
I write a decent amount of system-level software and this kind of pattern is unavoidable if you actually need to interact with hardware or if you need very high performance. I have probably written the unsafe keyword several hundred times despite only having to use Rust professionally for a year.
It's only “unclean” because you see it that way. In reality it's no more unclean than writing C (or Zig, for that matter).
> And no, "Arc" is not a perfect solution to ths because its performance is terrible.
There's no “perfect solution”, but Arc in fine in many cases, just not all of them.
> I encourage you to write a doubly linked list in Rust if you want to understand what I mean about "un-kosher" code
I've followed the “learning rust by writing too many linked lists” tutorial ages ago, and I've then tutored beginners through it, so I'm familiar with the topic, but the thing is you never need to write a doubly linked list IRL since it's readily available in std.
> this kind of pattern is unavoidable if you actually need to interact with hardware
You need unsafe to interact with hardware (or FFI) but that's absolutely not the same thing.
> I have probably written the unsafe keyword several hundred times despite only having to use Rust professionally for a year.
If it's not a hyperbole, that's a very big smell IMHO, you probably should spend a little bit more time digging into how to write idiomatic rust (I've seen coworkers coming from C writing way too much unsafe for no reason because they just kept cramming their C patterns in their Rust code). Sometimes, unsafe is genuinely needed and should be used as a tool, but sometimes it's not and it's being abused due to lack of familiarity with the language. “Several hundreds times” really sounds like the latter.
Also, Rust has a bunch of annoying warts but the borrowck ain't one of them (unless you're trying to write a linked list but it's not really something that happens IRL).
That was not my experience at all. I liked the language from the get-go. And the more i dug into it, the more i found to like. I really appreciated the design of Rust's iterators, and traits, and general language design, way before i stumbled into any major issues with the borrow checker.
I feel like this depends very much on the programming style and probably on the domain. But i found that programming in a mostly "functions and simple data structures" way, passing things down to other functions that need them, and just processing data instead of "modeling the domain" into mutable and confusing "objects", i didn't really come across many lifetime-related issues that the borrow checker warns about.
And the few that i did found, they were actually errors on my part, and Rust detecting them was quite helpful. Things like trying to mutate a collection while also iterating it.
So, IDK, YMMV i guess.
Stuff like string manipulation is almost as painful as it is in C.
You can do "pointer manipulation" in safe Rust if you need to - anything that uses arena indices is basically that, e.g. petgraph.
The language wasn't really designed for it though so it isn't exactly integrated into the syntax.
But controlling allocation precisely is quite difficult in "normal" Rust.
Tbh I don't think it is actually that far away from Rust. It's aimed more at low level stuff, whereas Rust aims to do every level. It is more "trust me bro" than Rust's "no I'm going to check" safety. And comptime is pretty different to Rust's approach (arguably better).
But apart from that they seem to have very similar goals.
Regarding string manipulation, Zig has slices and comptime-checked `printf`, so that makes string handling _massively_ more ergonomic than in C. But, yeah, managing the lifetime of the printf-ed is a pain in the back, and Rust is _massively_ more ergonomic there, if managing individual string allocations is what you want to do. (though one cool thing that Zig makes easy is comptime asserting that stack-allocated buffer has the right size for the given format string).
But, I do find Zig surprisingly ergonomic overall! For example, I have two Rust crate for writing glue code utilities, xflags (argument parsing) and xshell (shelling out). For TigerBeetle, I wrote equivalents:
* https://github.com/tigerbeetle/tigerbeetle/blob/main/src/std...
* https://github.com/tigerbeetle/tigerbeetle/blob/main/src/she...
What I found is that the Zig version is significantly easier to implement and to use, for me. In Rust, those domains require macros, but in Zig it's all comptime. And memory management is relatively easy --- flags live until the end of the program, shell is just using shell-scoped arena. I am contemplating rewriting my personal scripts at https://github.com/matklad/config/tree/master/tools/gg to Zig for that reason. Though, Zig not being 1.0 _is_ a problem in that context --- I touch my scripts only rarely, and I don't want to be on the hook for upgrades.
Some people can appreciate both. I actually like both languages for different reasons and I don't really understand why they're consistently being pitted against each other. Language wars are... for lack of a more appropriate and less-crass term... stupid.
Even if you don't have any intentions of using a new language in your day to day career, there's usually a few super-interesting morsels in there that you can add to your knowledge banks.
I've personally lost count of the number of times I took a concept I learned in another language or technology stack and applied it to my day job.
I couldn’t agree more!
Rust feels like a better C++ with modern tooling. I am a tool that works for it.
This is certainly a very controversial opinion. The two languages should not really be compared or grouped together, not for at least the last 15 years, but probably much longer. Modern C++ features have been developed specifically to address all sorts of things that most C++ developers want fixed from the "C" part of the language's history.
In what way?
Typescript is really just an optional type system for javascript. I don't see the analogy with C/C++.
Rust’s main point is memory safety which is why it’s also a preferred language to reengineer cryptography libraries.
I’m interested in the embedded space (along with AI applications) and believe rust will be a language of choice for the entire toolchain here.
So I’m definitely interested in what gaps you see.
Take a look at the Adacore site, especially the free books (PDF):
Ada for the Embedded C Developer
Embedded Spark & Ada Use Cases
Is it? Or is it a tool that won't bug you when you make a mistake?
Programmers tend to, as Djikstra noted[1] confuse ease of programming with allowing unforced errors.
I don't want tools to bug me EVER. This includes when I make a mistake.
When I want a tool's opinion, I'll ask for it - through a linter, a static analysis tool, etc.
>Programmers tend to, as Djikstra noted[1] confuse ease of programming with allowing unforced errors
And how long had Djikstra worked as a professional programmer?
If that is your true wish, may I recommend JavaScript? It's a language famous for not complaining about errors.
Or WASM/ASM if you want something closer to the metal.
I personally, want the undefined behaviour Chernobyl to beep before it melts.
> And how long had Djikstra worked as a professional programmer?
According to his wiki if you disregard his tenure at University of Austin, between years 1952 to 1984. So around 32 years.
He's literally first Dutch programmer. Yes. He was a programmer before it was a recognised job.
This isn't to say it's better or worse than zig. If you're developing software and zig works for your needs, go for it. I'd like to see it used on a 100+ person project to see how it might hold up to the problems that I tend to see in c++, which largely arise for people not knowing about constraints that are not tracked explicitly by the compiler. On projects with less people, this doesn't happen as often.
People keep saying that, but I've also seen many who've used Rust for years, in big projects, say the opposite: it gets slightly better as you learn to oblige the borrow checker instictively, but it remains always a kind of annoyance and a friction.
https://news.ycombinator.com/item?id=41944121
The TLDR is that there’s all sorts of invariants that the Rust compiler internals assume about Rust code (eg pointers don’t alias). In C such assumptions can’t be made. Unsafe rust requires you to uphold all the same invariants that safe rust does without any guardrails and it has more such assumptions than C. C however is uniformly dangerous - there isn’t a safe subset to write your code in.
One example would be that you could have something like:
let x = 5;
do_something(&x as *const _ as *mut _);
Now do_something might dereference and write to the pointer. Now you have non-local safety to reason about because if the compiler realizes you’ve tried to dereference a mutable pointer to an immutable variable it’s free to declare that UB and do bad unsound things (the non-locality is because the unsafe is inside do_something but the problem is with the pointer cast in safe rust). There’s all sorts of subtle things like that.There are fairly straightforward ways to disclaim these invariants to a greater or lesser extent as appropriate, such as accepting &Cell<T> arguments (or even, e.g. Option<&Cell<T>>) in lieu of &mut T. But the Rust standard library is not yet comprehensively built with this in mind, so this might be an area where Zig's library facilities have a real advantage unless an effort is made to address this gap.
Trying to write Rust as a noob feels like being in a blackout swamp, waist deep in muck, fighting through thick air and trying, but failing to run from danger. Being utterly constrained in every direction.
Writing Zig as a noob feels like having the clouds part to reveal a bright, sunny Spring clearing and suddenly being able to cover land in a cheerful group with clarity, purpose and focus.
[Edit] Of course, this is purely subjective, obviously a skill issue and YMMV
And C is basically high level assembly for the PDP, which has little in common with x86, for example.
On the other hand, every architecture that has tried to expose them so far has failed; nobody wants to manually apply the Tomasulo algorithm to their code, or manage shuffling data elements into and out of their cache hierarchy.
And food is just molecules, but we don't eat chairs because they're also molecules.
Like, to be clear: if you want to develop in Zig because you like it for whatever reason, you should do that. There is no world, nor will there ever be a world, where there's "one language to rule them all". This comment should not be read as "you should write Rust instead".
I just don't find any of your descriptions of Zig to be things that Rust is guilty of. You can (mostly) write the same things in each language, ship a static binary, and your users would never know the difference. IME you are generally as "in touch with the computer" in Rust as you are in Zig.
It is a very explicit language.
Everything you're describing is a stylistic preference, though - and doesn't contribute to bloat, which is what the parent comment was implying. If your program is bloated, that's on you to clean up - it doesn't matter if it's in C, C++, Rust, or Zig. Every single one of these languages has nothing that stops you from getting it right.
(A weird aside but the downvotes on this chain are just odd to me. I'm not telling y'all to not write Zig, chill already)
In single threaded, sure. To my knowledge you're not faster in multi-threaded contexts.
> Rust developers program in terms of traits, and borrow checker behaviours.
You don't have to do that, though. People have experimented with other approaches besides what you see in e.g tokio.
They could say it just really vibed with them but they don’t.
I don't know of any other class of engineering that spends this much time on such weird attachments.
I started with 6502 assembly and PET Basic in 1977, so for me, Zig is simpler, and jives more with me than Rust. OTOH, I chose Ada/SPARK for our product for the high-integrity, mission-critical aspect of our automation software. I programmed in Pascal back in 1988, and it has the same syntax, which I always found verbose and boring, but it is clear and structured and the concepts to achieve this level of high assurance are not too difficult. For ultiimate fun, I program in J/APL and now mainly BQN, so I am not a fan of verbosity!
I would have been more attracted to Rust if it had kept ML-like syntax from OCaml and not the Algol-like curly braces of C/C++.
At a more "normal" salary the 1.5-2x figure is pretty accurate in my experience.
The idea an employer has $17,000 in taxes they pay and overhead for an employer is absurd.
So that's around 3-4+ years of development.
(Also, that $154K might include significant employer expenses not typically included in salary, like, e.g. healthcare.)
TigerBeetle actually already donated around ~$100K this past year. We just didn't announce it.
With revenue increasing, we wanted to increase our donation to the foundation to $128K per year going forward, and Synadia were keen to match. The only reason we announced it this time is to encourage other companies to join us.
Finally, as I wrote in TB's post [1], both companies have already donated the first installment, and Derek and I have also both been donating as individual donors in our personal capacity since around 2018.
Hope that clears it up for you! :)
Please join us and consider donating (or increasing your donation): https://ziglang.org/zsf/
[1] https://tigerbeetle.com/blog/2025-10-25-synadia-and-tigerbee...
that a lot
For them it seems safety and QA is a large part of the sales pitch, so that seems worth it.
Fun fact, Hetzner were surprised at the size of the order, and sent us an email to confirm. ;P
[1] https://x.com/TigerBeetleDB/status/1841089728935821674/quote...
Also, Rust does support checked arithmetic and has stable toolchains.
Suffice to say, we know the intrusive memory and comptime patterns we use in our code base, and they wouldn't be as natural to express in a language other than Zig.
Rust is a great language, but Zig made more sense for what I wanted to create in TigerBeetle.
[0] https://matklad.github.io/2023/03/26/zig-and-rust.html
[1] https://lobste.rs/s/uhtjdz/rust_vs_zig_reality_somewhat_frie...
Since previous comment was edited. I would clarify that I don’t doubt the engineering capabilities, just the timeline. A from scratch database in _established_ toolchains take 5-10 years. The Zig toolchain also is going to be evolving in the same timeframe or longer. The codegen, linking, architecture specific bugs etc. Isn’t it double the effort to bring to bear in the market?
However, and we write about this also in the post, but TigerStyle and DST enabled us to ship TigerBeetle to production in 3.5 years, in less time, to a higher standard, and to be the first distributed database with an explicit storage fault model (Kyle Kingsbury added new storage fault injectors to Jepsen) solving Protocol-Aware Recovery.
Our Jepsen audit is here (also linked in the post): https://jepsen.io/analyses/tigerbeetle-0.16.11
For more on TigerStyle as a methodology, hope you enjoy this talk: https://www.youtube.com/watch?v=w3WYdYyjek4
- you still get extra benefit from Rust, but the magnitude of the benefit is reduced (e.g., no UAF without F).
- you still get extra drawbacks from Rust, but the magnitude of drawbacks is increased, as Rust generally punishes you for not allocating (boxing is a common escape hatch to avoid complex lifetimes).
Just how much tradeoff is shifted is hard to qualify unambiguously, but, from my PoV (Rust since 2015, TB/Zig since late 2022), Zig was and is the right choice in this context.
The build time of Zig seems like the most desirable piece worth deciding over. Developer time is money, but it isn’t weird to have multi-hour build times in a mature project either C, C++, or Rust. The correctness suite is a bigger time sink than the build though. When building a database, you could drive the build time to 0 and still have hours in CI.
Extensive compile-time capabilities that allow you to verify many other types of correctness add enormous value when building database engines. This is a weak part of Rust's story.
Your use cases are likely more complex, so I'm curious what I should be looking out for.
Zig gives you a lot of tools to enforce correctness in simple and straightforward ways, where as Rust comes with a lot of complexity. TigerBeetle isn't the first project to talk about this, Richard Feldman also points out similar advantages to Zig over Rust as the reasoning for the Roc compilers rewrite from Rust to Zig.
It’s a little OT, but interesting nonetheless.
Edit: I had to google what NATS.IO is. The marketing site is infuriatingly useless. Please, can we stop doing this?
Edit: At the bottom on the footer it says compare NATS to Kafka. It took me to a page that requires me to enter my email, name, and a message in order to download the white paper. I flipped over my desk in rage.
thank goodness for llms to spare us the marketing drivel!
Of course there's a chance it's hallucinating (more likely with this niche thing) but it's not like it's critical information anyway.
NATS - An application connectivity technology (L7). It was originally designed for low-latency M:N messaging, and that is still true today. In 2018, native multi-tenancy, clustering options, etc. got introduced. The persistence subsystem (JetStream) was introduced in 2021. It has a completely different design than Kafka, but with overlapping use cases. For better or worse, we get compared to Kafka a lot and virtually everyone who engages realizes the advantages and opportunities. NATS is much more flexible for application use cases, for example, it provides KeyValue and ObjectStore abstractions on top of the core persistent stream. There are a plethora of other details, but that is the basic gist. Overall, it has a lot of batteries included for building everything from embedded applications to large scale cloud-to-edge systems.
Synadia - The founder (Derek) created NATS. We are the primary maintainers of the project. We build products and services on top of NATS including a global SaaS where you can sign up and use "hosted NATS" with additional features. We offer a BYOC model, one of which we manage for you, or a Kubernetes-based self-service one that you deploy yourself. We also support fully self-hosted for customers that need to run in their own data centers or at the edge.
Regarding the comment re: the website, there are improvements we have in the works. Happy to engage and help clarify anything that is confusing.
You probably have an influx of traffic that you could convert to customers through “what is this? -> oh cool I could use this!” pipeline if the marketing website enabled that.
What is NATS, how does it compare to other similar software, and why use a hosted solution… all this should be easily found.
And if I see a “enter name and email to download a resource” it just immediately turns me off from even engaging with the site.
You can find the NATS site here [1]. NATS is so flexible it's admittedly easy to get lost in what exactly it is or what it's good for. The easy explanation is it's a message broker that can let programs send and receive messages to each other. Unlike a message queue, it has no persistence. Clients can that express "interest" in a topic receive messages to that topic, but messages disappear if nobody listens to them. It's kind of like UDP without addresses. It's clustered and supports complicated topologies where clusters route messages to each other, and has a powerful ACL system for exposing clusters to unprivileged actors that shouldn't be given full access (which means you can easily make it multi-tenant). The closest comparable technology might be MQTT, and indeed NATS offers an MQTT mode.
NATS also has a bunch of higher-level stuff built on top of it: A Kafka-like "stream" feature called Jetstream, RPC, a key/value store, an object (blob) store, and so on. The NATS message bus is the core primitive that these features use.
[1] https://nats.io/
The BDFL point is particularly interesting to me having followed C++ for almost two decades and having been disenfranchised by the inconsistency of the design. I am more of the belief now that a BDFL is the right model for programming language design, particularly one that isn't insular and listens to feedback, but upholds their vision for the language above all else.
Such an excellent summary. I've been trying to communicate this regarding the difference in Rust and Zigs approach to memory safety, and Joran does it so much better than I ever could.
How about compile speed? Are you willing to wait an extra second to get 100%? How about 10 seconds? A minute? 10 Minutes? An hour?
Rust is notoriously slow at compiling and people have been banging on it for a while. At some point, you have to accept that the language, itself, has properties that make compilation slow.
Rust is 100% memory safe in specific places yes, but much less so when you have to dip into unsafe Rust. Unsafe memory access will always exist, you can't do anything about it. If you need to interact with the underlying system it's just something you have to deal with it.
Zig on the other hand isn't 100% safe in any one part, but it's 90% safe in nearly all parts. Zig recognizes the outside world exists, and makes it easy to write safe correct code given that reality.
Time will tell, but on average I suspect Zig and Rust will produce equally safe and correct software. The further away you get from the hardware the bigger Rust's advantage is, the closer you get to the hardware Zig has the edge.
No hidden allocations is a big one. If something allocates you have to explicitly give it an allocator. This makes tracking allocations much simpler. Combined with the test allocator you can also detect memory leaks.
Allocators also simplify memory management patterns. For example, it's trivially ease to use an arena. Then all your little allocations can be freed all at once, eliminating the need to track each individual allocation/free.
The defer keyword allows you to place the free call near the allocation (same with other resources that require closing). This isn't always possible of course, but it is a lot of the time and makes it easier to track.
Bounds checking is another big one. While loops/for loops with capture groups means you'll basically always see Zig programs iterating through arrays/slices in a manner that ensures you can't go out of bounds.
In terms of general correctness you have explicit nullability. If something can be null you have to mark it as such directly in the type. Then when accessing it you have to unwrap it and deal with the null case.
Same for errors. If something can return an error it's indicated in the return type. The error has to be unwrapped and then thanks to tagged unions you have to deal with all the different possible cases.
There are language conviences like the try keyword that let you side step some of the explicit handling, but even those are easy to track. And you know you're doing something that might bite you.
All of that adds up with a dozen other design choices to produce a language that makes it a lot easier to produce correct code.
Edit: In terms of concurrency I have to give you that one. The language is still pre 1.0 so we'll have to see what happens.
- No hidden allocations: Other languages already provide abstractions for this. Are hidden allocations really a significant issue in modern systems languages, even when managing memory manually? This concern is distinct from memory leaks or general memory safety problems.
- Allocators: It's easy to use an arena in C++ or C.
- defer keyword: Similar constructs have existed in other languages and even as a GNU C extension for many years.
- Bounds checking: Not an issue in Rust. The real source of risk has always been raw pointer manipulation, which Zig doesn't (cannot?) solve. How is it better than address sanitizer? Rust solves this at the language level.
- Nullability: GCC has long supported non-null attributes and unused-result annotations and Clang provides _Nullable. C++ goes further with type-level guarantees via std::optional, gsl::not_null and related abstractions.
- Errors: Zig’s approach is clean but equivalent mechanisms exist in C++ (std::expected) and Rust (Result and error types). It’s mainly cumbersome in plain C.
All of these are useful quality-of-life improvements but they don’t address my problems in systems programming (such as efficient and composable data structures, data movement, scalable concurrency, safe memory management ...and boiler-plate, lots of boiler-plate). These problems are easier to solve with a type system that allows me to make strong correctness guarantees and extend those guarantees.
Lastly, I’m unsure that any potential boilerplate reduction would be significant if implemented in Zig.
I don't think explicit use of allocators is distinct from memory leaks/safety. In Zig you will always know with 100% certainty that something is allocating memory. Making it easier to track what needs to be freed. This isn't always the case with C/C++. Also, as I said before, thanks to the way allocators work, Zig can provide a testing allocator which actively helps detect memory leaks. Their may be tools to help with this in languages like C/C++, but in Zigs case it's build right into the testing framework (which is also provided by the language).
To your point about arenas, or other memory management techniques, it's not that they aren't possible in other languages. Obviously they are. But I think it's safe to say that the default memory management mechanism in C/C++ is malloc/free. This leads a lot of people down the path of lots of little allocs that all have to be tracked and paired with a call to free. In Zig, by design, any method that can allocate should take an allocator as an argument (or you pass one when the struct is created and it uses it, but this style is falling out of favor with the community). This pushes developers towards alternative methods of memory management, encouraging them to not just fall back on mallow/free. Arenas, fixed buffers, and other mechanism aren't just possible in Zig, they are first class citizens.
I think there is a huge different between "defer" being part of the core language and being part of an extension. By being relegated to an extension it's usage is naturally going to be greatly reduced. The only two other languages I can think of that have this feature are Odin and Go, and Go is garbage collected.
Yeah, in Zig you're directly manipulating pointers. That's just how the language is designed. It tries to make it safer, but it doesn't eliminate it, because it's not trying to. If you think this is a bad thing, that's fine. I'm not going to try to change your mind on that point.
For nullability, everything you describe is opt in, and tool specific. It is baked into the type system in Zig the same way it is in Rust. It's just handle in (imo) a simpler and more straightforward way.
Zig's errors as value approach isn't that much different from Rust's in spirit, and I think both Zig and Rust outclass anything in C++ in this regard. It's an option in C++, but mandatory in Rust and Zig, which I think is where the real power comes from.
Ultimately, if you prefer Rust I'm not trying to change your mind. I was just trying to address your question. I don't think any of Zig's individual choices haven't been made in other languages, but I think the way Zig composes them all produces something novel and intriguing. All these little choices push developers in the right direction at every turn. Zig is still young, and it has plenty of rough edges to sand down, but I'm interested to see where it goes.
Like I said previously, I think in the long run will see the average safety/correctness of Zig programs be on par with Rust's. I think the two will just fill different niches.
I'm not trying to change your mind either; I'm just trying to understand the enthusiasm around Zig. There's a lot of it! To this day, I still don’t see what hard problems it would help me solve (that other languages make solving easier). At least for me, there’s little novelty or technical intrigue in that sense for any "systems" problem I've worked on.
- Explicit use of allocators: In my experience, hidden allocations rarely cause memory leaks. The real issues are forgetting to free memory, freeing the wrong pointer or managing overlapping objects. Some systems I’ve worked on also use different allocators or representations for the same types (e.g., for succinct encoding). Stronger type systems and optional automatic memory management make life easier when available.
- Arenas: malloc and free are as first-class as any other allocation mechanism in C. In C++, using alternative allocator types takes a bit more finesse. It’s an opt-in model rather than the default but still a relatively light lift.
- defer: I agree that its use would be significantly reduced but it’s easy to opt into. Many languages provide similar functionality either natively or through libraries, including Rust, C++, Python, C#, Java, D and Swift. Even C has this through GNU extensions (for fun, check out how far the Linux kernel takes this concept).
- Pointers: I love pointers but what helps performance more is explicit typing, stronger provenance and alias analysis.
While I'm not specifically advocating for Rust here, I don’t believe the average safety or correctness of a Zig program will match that of Rust. Rust is memory-safe by design, whereas Zig is not, and Rust also provides elegant solutions to several genuinely hard problems from memory management to events to scalable concurrency and an expressive type system.
I honestly don't think Zig is doing anything big and flashy, or highly revolutionary, the same way Rust (or even a project like Mojo) is. The stated goal was essentially to produce a modern C that pushed developers towards writing reliable software. I think it's hard to explain the enthusiasm because it's kind of greater then the sum of it's parts in some ways.
There's other stuff we haven't talked about here too, outside memory safety. It has incredible interopt with C, and the Zig build system is ridiculous. I don't know any other build tool that has the ability to handle cross platform builds for not only itself, but also C, in the way Zig does.
To be honest, I think a little of it might be the backlash to Rust as well. Portions of the Rust community (not all of it of course) are down right exhausting. And treat memory safety like it's some kind of moral imperative. I think there is a group of people who aren't enthused by Rust, and annoyed by the vocal minority of the community, that are glad to have something that is more suited to their sensibilities.
Both are fantastic languages and I hope to see them both evolve in years to come. Zig has a longer road ahead but it really is elegant and simple to work with.
I’m particularly interested as I’ve encountered similar challenges with intrusive data structures in my own work.
This isn’t intended as flamebait. I’m trying to understand Zig’s long-term positioning and design philosophy. I have serious confusion about the type of problems Zig is aiming to solve. In my view, Zig is not solving the actual hard problems in systems programming and it doesn't have the foundation to either.
Memory safety? Still entirely manual. Race conditions? Nothing in the language prevents them. There’s no ownership model, no lifetime analysis, no way to tie resource management to the type system. Compare that to Rust’s borrow checker or modern C++’s RAII and concepts. Zig’s type system is shallow. comptime is nice for generating code, but it doesn’t give you formal guarantees or expressive power for invariants, safety, or correctness.
The type system itself has no serious formal grounding. It can’t encode complex invariants, can’t track aliasing, can’t enforce concurrency safety and can’t model safe resource lifetimes. These aren’t academic extras — they’re exactly what decades of research in programming languages, operating systems and concurrent computing tell us you need to scale safety and correctness. Zig ignores them. Performance? When the policy is in the type (allocator choice, borrowing/ownership, fusion shape), Rust/C++ compilers can specialize, inline, and eliminate overhead. In Zig, the same policies are usually runtime values or conventions, which means more indirect calls, more defensive copies and fewer whole-program optimizations.
Concurrency is another major gap and in a real systems language, it cannot be an afterthought. Even if Zig isn’t currently aiming to solve concurrency or safety, a “serious” systems language inevitably has to, because these are the problems that determine scalability, maintainability and security over decades. The async model in Zig is little more than manual coroutine lowering: the compiler rewrites your function into a state machine and leaves correctness entirely to the programmer. There’s no structured concurrency, no safe cancellation, no prevention of shared-state hazards. Without a concurrency model that integrates into the type system, you can’t make guarantees about thread safety or race freedom and you end up relying entirely on discipline (which doesn’t scale).
Even in its most-touted features, Zig seems to be solving syntactic sugar problems, not the important systems problems. defer and errdefer? They’re effectively cleaner syntax for patterns C has had for decades through GNU’s __attribute__((cleanup)) or macro-based scope guards. Error unions? A nice alternative to out-parameters but just syntactic polish over an old idea. comptime? A more integrated macro system but still aimed at reducing boilerplate rather than providing deeper correctness guarantees.
The allocator interface? Another missed opportunity. Zig could have made it type-aware, preventing allocator misuse and catching entire classes of errors at compile time. Instead, it’s basically malloc/free with slightly cleaner function signatures. No safety net, no policy enforcement.
Zig discards decades of research in type systems, concurrency models, safety guarantees, and memory management, then reimplements C with a few ergonomic wins and leaves the hard problems untouched. It’s a restart without the research and not systems language evolution.
I am not a Rust fanatic but by contrast if you’re moving away from C++ or C, Rust actually tackles the big issues. It enforces memory safety without a garbage collector, prevents data races in safe code through its ownership and type system, offers structured concurrency with async/await and has been battle-tested in production for everything from browser engines to operating systems to databases. It is built on decades of progress and integrates those lessons into a language designed to scale correctness and performance together.
In my own code (primarily C++ and Rust), Zig wouldn’t solve a single core problem I face. Memory safety would still be my responsibility, concurrency would still be entirely manual, performance tuning would remain just as challenging and the type system wouldn’t catch the subtle bugs that matter most. The net result would be cosmetic changes paired with fewer correctness guarantees. Even C, for all its flaws, is better defined than Zig (both in having a detailed, standardized specification and in benefiting from partial formalization).
I am eager and optimistic that Zig starts taking itself seriously as a systems language. With new talent, deeper engagement with existing research and a focus on solving the actual hard problems, not just smoothing over C’s syntax, Zig could grow into something much more than it is today. But until then, the question remains: what problems is Zig actually solving that make it worth adopting over Rust or even modern C++? What concrete systems programming problems has Zig’s development team personally run into that shaped its design and are those really the most critical issues worth addressing in a new systems language?
If all it offers is nicer syntax over the same old pitfalls, I don’t see it and I don’t see why anyone betting on long-term systems software should.
What am I missing?
But sincerely, I think we don’t share the same philosophy:
End to end correctness of software is a systems design problem, not a language problem.
I believe that if you try to shoehorn too much safety into a language (100% vs 90%) you get into trouble, and don’t solve safety end to end, like we try to do in TigerBeetle, systematically through TigerStyle, Deterministic Simulation Testing of the system as a whole etc.
I try to write about much of this in the post, to share our ideas, but it comes down to power-to-weight ratio, Zig’s essential simplicity, and recognizing (and coming to terms with) the fact that:
No language can solve end to end systems safety. For this you need systems thinking.
The programming language is part of the system design. The abstractions, invariants and guarantees the language provides define what classes of bugs are even possible to have. For example, Rust’s ownership and lifetime semantics eliminate entire categories of memory and concurrency errors that would otherwise surface as “system-design” issues in C or Zig.
When you say “power to weight ratio”, could you elaborate on how that applies relative to C++ in the context of TigerBeetle? You mentioned io_uring support being added. What makes Zig uniquely suited for that compared to a more mature language like C++, which already offers a concurrency model and a sophisticated type system you can selectively use?
You also mentioned prefetch support. That's a lot easier to implement in other languages. I’m curious what specifically made Zig the better fit for these optimizations in your experience.
I appreciate you taking the time to respond to my question.
Let me invert our roles!
What are 3 of some of the hardest correctness problems in TigerBeetle—and how does TigerBeetle solve them?
Hint: None of these would be solved by language.
But the language defines the failure surface and the cost of getting those systems problems right. Likewise, the difficulty and performance of implementing correctness mechanisms (lock-free data structures, concurrent logs or safe async I/O) are directly shaped by the language’s semantics and toolchain.
So while I agree end-to-end correctness is a systems-design problem, the language choice determines how much of that correctness is enforceable, how much is manual and how hard it is to make the right design fast and reliable.
The “harder problems” in TigerBeetle then (and where we invested millions literally in engineering):
- distributed system strict serializability (cf. our Jepsen audit)
- storage fault safety (TB expects disks to write/read to/from the wrong location, or drop writes entirely, and MUST survive this, all while preserving strict serializability and maximizing availability given the redundancy at hand—it’s one of the first databases in the world that can solve this to these tolerances, cf. “Protocol-Aware Recovery for Consensus-Based Storage”)
I will leave the third one to you! :)
But C++ (or even writing TB in a memory safe language like JavaScript—to reduce the point to absurdity) would have done nothing to solve the hundreds of distributed system bugs our simulators (and TigerStyle methodology) find, ~none of which are language bugs.
Again:
Distributed systems end to end correctness is a systems design problem, not a language problem.
What do we want from our language then?
Power to weight ratio, literal power over the metal (e.g. no OOM or hidden allocations or copies), with essential simplicity in syntax, and explicitness in everything.
I know of no better language for TigerBeetle in this than Zig.
But to make the point more strongly, I don’t think we would have succeeded as a project and company if we hadn’t picked Zig.
C/C++/Rust I feel almost certainly would have cost more, but also would not have given the same quality in terms of design and what I wanted to explore in TigerStyle. It simply would not have been TigerBeetle. We truly needed Zig to exist, and I’m glad it did at the time we needed it.
praveenperera•3mo ago
derekcollison•3mo ago