One example - I prefer the named fields of Zig over Odin's use of positional fields. Zig's syntax is the more popular one as of late, heck even C has named struct member initialization.
IMHO Positional is just asking for trouble. Yes it works fine for game programming, and not having to specify x and y thousands of times in a code base is nice, but for general purpose languages where a struct can have a dozen+ fields to init, positional assignment is how bugs are made.
I didn't find the Zig language benefits sufficiently compelling to learn yet another opinionated language.
Since much of my work is with embedded and bespoke systems, I did find the Zig cc front-end to LLVM convenient. Maybe it's because I haven't sufficiently mastered Clang for cross-compilation.
Moreover Zig is a research playground. Ideas will find their way to D/Nim.
Zig CC is another innovation.
The compile time is also a great experiment.
It brings a lot to the table.
May not be a total C replacement, but it is an improvement and lots of C code, can be written in Zig. Lots of people can be introduced to Systems Programming with Zig. For Low Level coding, have you heard of FASM? Better than C in my opinion.
I just wonder how well it will work long term with update cycles and such. Zig and C3’s decentralized model seems easier for handling frequently updating libraries.
Uhm, what does this have to do with C/Zig?
Also, the compiler (and what else?) isn't even implemented [1].
[1] https://github.com/profullstack/smashlang/blob/master/src/co...
> Website Access Prohibited
> Malware Website Warning
> The website you are trying to access is known to host malware and poses a security risk to your computer and network. Please contact your IT help desk to review if you are impacted. If you think you are receiving this message in error, please contact your IT help desk.
I guess you should know your website is seen as a malware vector.
My main qualm with it is that it doesn't try to integrate with existing C programs. The ideal C replacement for me would be a two-way per-file C transpiler, in the same way as the Vala language was. Cython also works the same way even though it's not a C replacement.
Having to know the full set of Zig sources in advance due to error types, and the strong accent on Zig as a build system, are both a non starter for integration in existing large C programs, for example.
Mine is not necessarily a criticism of Zig, mind. It's a criticism of what is needed from a C replacement.
> Having to know the full set of Zig sources in advance due to error types
You can just catch all of the errors and translate them back into errno or whatever homegrown scheme your c project is using.
- - -
Does the zig compiler not support generating .o’s without a build.zig? I like the zig build system so much that I’ve migrated some large projects that will likely never contain a line of zig, so I haven’t felt that pain point.
Of course it does, all the build system does is orchestrate invocations to the other basic zig subcommands (`build-exe`, `build-lib`, `build-obj`, etc). You can invoke `zig build-obj` and get an object file in whatever flavor you prefer.
%.o: %.zig
zig build-obj *.zig -o *.o
Not sure I get OP’s complaint then.If you don’t want to learn or debug in a new language then you should probably just stick with C?
The goal of zig as far as I know is to make it so you can transition away from C completely. Maintaining a C target seems antithetical to breaking ties with the “C machine”.
This was one of the main things that bothered me about Zig when I last tried it. This actively works against me when writing prototypes, exploratory code, and especially when writing code to learn the language. I get the rationale, that it's trying to protect me from shipping poorly designed code. But helicopter mom features do not belong in a programming language. They are just as hindering to progress as a literal one would be to a grown man, and for the exact same reasons. I know Andrew has made up his mind, but minds can be changed. So please, Andrew, please take away this helicopter mom feature.
It's more like the cashier smacks a peanut out of your hand saying you'll get fat.
Thanks for the hearty laugh :D
But even from an utilitarian perspective, compilers do have warnings and they could just have used that.
If there are multiple people in a car and some choose to wear seatbelts and some choose not to, those who are not wearing seatbelts become a danger to everyone else as their bodies become in-vehicle projectiles.
Sure, I can understand the debate when it's just a single person in a car. But when a person's decision starts impacting others the debate is going to be very hard to win.
Like take the car crash out of the equation and imagine some cars came with an ejector switch that launches you through the windshield at 70 mph. This would not be allowed.
Your new car sounds an alarm non-stop if any seat belt in the car is unfastened, whether or not a seat is occupied and whether or not the car is even on. This seems kind of silly, so you raise it with the manufacturer and they say that standard procedure is to just leave all seat belts fastened all the time.
You start doing that and quickly realize that it's much harder to remember to put your seat belt on than it was in your old car, because the car doesn't warn you when you've forgotten your seat belt and when you do remember on your own you have to sit up awkwardly to unfasten it before you can get under it and fasten it around you. You point this out to some fans of the manufacturer and they act like you're the weird one for not seeing how this makes you safer.
The worst part is, the recommended way to avoid these compilation problems is to add:
_ = my_unused_variable;
to your code. Some IDE extensions even do this automatically. But if you do that, it permanently suppresses all compiler feedback that my variable is unused! Its basically the worst of all worlds:- I can't easily prototype, or test code as I go because I keep stubbing my toe on irrelevant unused variables. And these are compilation errors, not warnings.
- In order to test my code, I need to go and fix all the unused variable errors (either automatically or manually). But once I've done that, they're essentially suppressed forever. Now I don't even get warnings, and I'm at risk of shipping code with unused variables. Now I need to manually scan my code, looking for times when _ = x was only added as a placeholder. Will I forget some? You bet.
So I end up in a situation where its both less convenient than other languages (I have to do more work to test my code). AND I'm more likely to ship bad code - since by the time I'm done, half my unused variable warnings have been manually suppressed and I can't find out where they are.
This obviously works for some people. Maybe some people never try running their code as they go. But its just horrible for me. It breaks my workflow and risks me shipping worse code. And it provides no actual benefit in return.
I feel like that's already the cultural precedence in Zig. I mean, isn't their primary way of making sure your code fully and oncely frees all memory, just writing tons of tests that thoroughly exercise your function(s)?
The problem is that zig makes it hard to run the first half of a function or block of code. I've always got variables or parameters that I simply haven't put to use yet.
In an ideal world, my half-written code would fit neatly into a set of well defined functions with no unused variables. But at an experimental stage, I haven't refactored my code to make it beautiful yet. I always prefer to debug my code before refactoring it, because if my tests fail afterwards, I can isolate the bug to the refactoring process itself.
> at an experimental stage, I haven't refactored my code to make it beautiful yet. I always prefer to debug my code before refactoring it, because if my tests fail afterwards, I can isolate the bug to the refactoring process itself.
Yes, that is inherently the best way to write code. The right flow is,
1) make it work
2) make it work right
3) make it work for the right reasons
All programming languages that claim to be alternatives to C eventually become themselves, zig is good enough and different enough to not care about being an alternative to C!
And also: I don't quite understand most of the Arguments. UB is often a way to ensure that certain optimizations can be made (if i remember correctly.) Also there just are certain facts that when you have full control over memory, some things can just not be predictable. (Accessing invalid pointers as an example)
Verbosity is mostly subjective. Most of Author's concerns were path lengths, which can easily be "fixed" by redefining the path into a constant. `const debug = std.debug;` I also think the Argument towards "it looks like java's println" full on is a bad argument. Especially because `std.debug.print` does something completely different.
The whole "critic" towards `build.zig` also seems like it's done with without good faith. Yes it's daunting at first. Yes it requires to learn about the build system in the beginning. Sure those are all very real Arguments, author just doesn't want to mention the benefits of it.
Then half of the arguments about comptime i also don't quite understand. It seems like it is building up the main point of the argument, but then you just get hit by a statement. So no explanation no nothing. Talking about lazy evaluation and a little bit of how SoA makes things worse (which i have no clue how that matters in this context) and then saying it's violation "no hidden control flow" is not really getting the point i feel like.
He just pointed out to us language skimmers that it's a potential chicken egg problem, which we may not have considered. Perfect for this kind of article. Whereas pointing out its merits would just be recreating part of ziglang.org
Out of my head, the benefits of having a `build.zig` that you learn right from the get go is, that you kind of have to dig a bit into the eco-system a bit. Also how the buildsystem works, etc. which then in return makes it muuuuch easier later on if you have a bigger project, to properly utilize the power of the buildsystem.
I can also only consider my perspective here, but I've done quite a bit of Rust now (and work with it for over a year now, full time as well), and i can tell you as much: I would be thrown into icy waters if i need to do anything with a build.rs.
You'd need an OS that supports it, but it seems viable that the OS using allocators could know what happens at any given pointer.
To do it statically though, and with enough detail, I suppose it would take something like Idris with dependent types, and I'd imagine at that point it would be a pretty tough exercise.
Dependent Types could help, although i am unsure if it helps with raw pointers.
Zig is one of the strong contenders, but Odin is IMO following a saner path, Carbon and D should be mentioned and I think there are a few others.
My initial reaction to new programming languages is naturally skeptical, but I really liked the way the designers behind those efforts approached it, trying to solve frequent C/C++ pain points and adding a few fresh ideas.
But overall, after a few years, I am sticking with C and orthodox C++, the cost/benefit ratio is simply not worth the effort, none of them are really mature and the problems they try to solve are not painful enough.
My current take on this is to view Jai/Zig/Odin/etc. as attempted C forks, quite often motivated by hubris and coming with the usual inconvenience of forks, a lot of wasteful/redundant efforts put into small islands, each ruled by their own "benevolent" kings.
An other analogy that can be made is the many attempts at replacing keyboards layouts. Dvorak etc. They probably have their merits, but the adoption was limited to a few enthusiasts for the same reasons that C is unlikely to get replaced by any of those forks.
Didn't Rust do the same thing? We only call efforts wasteful until they become mainstream, then in retrospect we call it excitement driven prudence.
Anyway, is there an "Odin for C programmers" short guide, so I can learn it more quickly?
Eh, I don't know, I don't think so absolutely. I think in the general "application" paths yes there's less complexity but as a language overall, no.
The thing is Rust has all the same capabilities of C++, that is to say, everything. With that same strong emphasis on free abstractions - meaning, abstractions that do not cost anything more than if you wrote the implementation yourself.
Templates and stuff are a rabbit hole but if you go down the path of procedural macros and generics in Rust it's more of the same. It gets crazy, wild, and super complex. But most people don't have to worry about that, because these sort of "squeeze every last bit of juice" features are most useful for library authors.
This should be called hardening and the whole "unsafe" thing is also detrimental. Computer security and exploits is a highly complex topic, there are no silver bullets. Rust code is safer to some extent, but not "safe" from bugs and exploits.
No one is out here building C with Safety.
The lack of safety is not painful enough? There is an entire industry (i.e. job sector, training, expertise) that evolved to try to thwart bad actors abusing unsafe languages.
Computer security and exploitation is a complex and difficult topic.
Bad actors are looking for vulnerabilities to build exploits, some are related to memory corruption but many of the most sophisticated ones are not.
All programs are potentially unsafe, regardless of the language. Using a language like Rust can help to some extent, but it won't be sufficient if the programmer is too ignorant about code hardening.
You can't replace experts with the flip of a switch.
Odin, a pragmatic C alternative with a Go flavour - https://news.ycombinator.com/item?id=43939520 - May 2025 (106 comments)
That's a long-winded way of saying that programming language preference is mostly a matter of personal taste as well as more objective but non-intrinsic, "environmental" or social factors. But that doesn't mean that preferences are evenly distributed. Even within a specific domain there may be big disagreements among programmers over which language is "better," but some languages may appeal to significantly more people than others. Programming language designers, at least those who target their language for wide audiences, effectively try to pick tradeoffs that they guess would appeal to many.
This is what makes statements like "but as we all know, it’s not always the best alternative that wins" meaningless. Winning means appealing to more people, i.e. having a good "market fit", whereas "best" has no agreed-upon definition. Like in the famous story about Betamax vs VHS, one had a better picture quality at the expense of recording time, and it turned out that a longer recording time was more important to them than better picture quality. In various debates over programming languages, I often see people frustrated when confronted with the reality that other people prefer different tradeoffs to their own preferred ones.
What the author find surprising or confusing in Zig are things others find desirable (for reasons such as making the language more expressive, simpler, more easily portable, or faster to compile). If you prefer Odin or Rust or Jai -- use them; that different people prefer different languages is very much to be expected. But one of the things I, subjectively, find appealing in Zig has to do with the point made in the post about "high level conveniences" and "abtractions." Over the past several decades, high-level languages have become so fast that in most software it makes little sense to use low-level languages regardless of the "high level conveniences" they may offer. But low-level languages still have a very important role to play when full control over resource usage (especially memory) is needed, such as in constrained environments. That is why I, as someone who does both high and low-level programming, prefer a separation between the two. I probably won't write a large, complex application in any low-level language, and when I do write low-level code, what's important to me is to clearly see what's going on. Anything that hides that from me isn't a convenience but an inconvenience when doing this kind of programming. To me, subjectively, it seems that making low-level languages appear more high level is a mistake -- it hinders low-level programming without offering a truly compelling alternative to a high-level language. Whether my preferences or the author's are more common is something we'll find out in another decade or so.
[1]: One of the things I find annoying -- not only in programming language debates but in science and technology discourse in general -- is invalid extrapolation of valid empirical results. For example, we do have some valid empirical comparisons of TypeScript vs JavaScript or Rust vs. C, but extrapolating from those to conclusions about, say, TypeScript vs. Clojure or Rust vs. Zig requires making unfounded (as yet) assumptions (such as that all untyped languages are alike or that all languages that don't soundly prevent all undefined behaviour are alike).
Hi pron, I am hoping you could write your thoughts on Odin.
There are some very successful uninteresting languages (Python and C# come to mind), but they don't give you much to think or talk about other than, perhaps, a particular interesting feature here or there. Again, that's not to say they're badly designed or don't deserve their popularity.
One reason is that we've built most of the foundations of modern software already in these languages. New apps pop up all the time but they all rely on ffmpeg, etc.
The only way for a new language like Zig to cement itself is to build foundational software that other programs depend on. I don't think even Rust is moving in that direction (lots of Rust apps, but not so many libs I've had to use in my programs that were written in Rust).
This might be controversial, but I think its fine for small languages to exist, with small followings. Nobody will ever make a browser in Haskell, but its still a work of art. Nobody uses Elm, but React wouldn't exist without it. And so on. Rust is useful on its own. But its also already inspired the C++ language committee to start talking about memory safety a lot more seriously. Thats a big deal!
I'm delighted that languages like Zig exist. If for no other reason than to show that if you're dedicated enough, a lone hobbyist can still invent a language and make it good. (I wish that were still true for web browsers.)
Native rust extensions to provide canonical high quality interfaces for other languages, like Valkey Glide are another example.
Adding on the fact that rust is the fastest or nearly the fastest growing language in terms of users, and I can’t agree with the view that it is not moving in that direction.
I don't think zig best-practices are written in stone yet, but I would expect ReleaseSafe to be the default in production. Since zig allows turning off/on safety checks on a per-block basis, you only need to pay as much of a performance cost as you want for this.
Actually, this doesn't really come across as a sincere critique of zig. It's more like how a sport fan talks about a rival team (I guess the author must be team Odin). The "first impressions" section is silly.. of course you don't understand how it all works before you've learned anything about it. It might be a bit confusing and have some new patterns. And yes, all the options of the advanced build system is complex, but... you can use the simple build mechanism, or just go with the template for that matter.
Myself, I think there are certainly valid criticisms of zig to make and I don't know if it's ultimately going to amount to something, but you've got to have some knowledge of the subject and not come at it with a predetermined conclusion.
Which profile is the default for the Zig compiler itself? What about Bun? TigerBeetle was the only one of the three to use ReleaseSafe by default last I checked? (Edit: in fact, they outright ban ReleaseFast and ReleaseSmall because they care about correctness.) ZLS suggests ReleaseSafe in its README too, if you count it among the prominent Zig projects.
My counter to this is: who gives a shit? I'm not writing a vector library or a game engine; I'm writing systems code, and unnamed initializers are harder to read and expose code to subtle bugs. And for what? So that vectors look prettier?
A lot of programming language design seems to prioritize making its own standard library math functions more elegant to implement, which I find to be very irritating, and Zig is a breath of fresh air in this regard.
Oh man, I wish the Haskell crowd gave at the very least lip service to that type of idea.
jbritton•9mo ago
Although I’m intrigued by another idea I haven’t thought through. Haskell requires all function side effects to essentially propagate a description of the side effect up to main() where Haskell executes the side effect. There is also the idea of imperative shell, functional core that I don’t fully grasp. In any case perhaps all side effects should execute at the top level or maybe in a parallel environment and here passing an allocator would not be so onerous.
bnolsen•8mo ago