I finally got it working. I had to flush both the encrypted writer and then the stream writer. There was also some issues with reading. Streaming works, but it'll always return 0 on the first read because Writer.Fixed doesn't implement sendFile, and thus after the first call, it internally switches from streaming mode to reading mode (1) and then things magically work.
Currently trying to get compression re-enabled in my websocket library.
I'm not a Zig PM but the first obvious fix for the issues the OP wrote about is to write better documentation, including usage examples (the more the better, almost to a fault). Also doubles as a good time to reflect on whether the user is having to do too much.
If the tradeoff was absolute performance/avoiding introducing load-bearing performance-lowering abstraction I think that goal was achieved, but DX may have gone out the window.
You’re not familiar with Zig’s culture, I guess. Complain about the lack of documentation and be prepared for the flood of “just read the stdlib code” helpful comments by pretty much everyone who writes Zig right now.
Because most APIs are just as hard to use as in this post (check things like HTTP and even basic file system operations) only the strongest survive.
I am just editing docs now that Claude Code writes for me. I am fanatic about developer docs (and I guess an exception as I love writing them) but with a set of concise instructions for CC and some writing style examples I get 90% there, sometimes 99%.
If you believe you don't have time for the last 1--10% you should not be in charge of writing any API used by anyone but yourself. Just my two c.
Ai is great at block comments, there is no excuse. Add to that a small anotated usage example written by a human and this whole post would have not existed.
Lack of docs also cripple AI from understanding, so future adoption becomes even more bleak.
If an api or library developer didnt bother doing even bare minimum docs, my confidence in the library drops aswell.
Did they skip testing aswell? Ran the happy path for a day and called it good?
This post sour my interest in zig. Its now obvious to me now why rust took much of its market.
I like Zig, but if we look at the numbers, the difference is probably more to do with funding that anything:
Zig (programming language) - First appeared 8 February 2016; 9 years ago
Rust (programming language) - First appeared January 19, 2012; 13 years ago
Also, Zig at this point isn't really a brand new language anymore. I have comments on their issues dating back to 2018, so it's been a very active language since at least then.
Those are not comparable dates. The Zig "first appeared" date is a few months into development by Andrew in his spare time. The Rust "first appeared" date is after 3 years of development by Graydon in his spare time, followed by 3 years of development by a Mozilla-sponsored team of engineers.
That would hurt adoption. I understand things move fast but if you want people to make the switch other than hello world, it has to be at a minimum cosy. Sending them to hell and find your way out isn't a good move long term.
I tried Zig a couple of times and I got that feeling: very powerful and clever language but not really for me, I don't have the headspace, sorry. I need something I can debug after an 8 hours dayjob, a commute and having put the kids to bed. It better be inviting & fun! (Hi, C).
Yeah, thinking about this attitude positively, maybe it’s a feature — if only hard core people can comfortably figure it out, you get higher quality contributions?
Not trying to imply that’s an explicit goal (probably instead just a resource problem), but an observation
I think it is a trade off for between zig's development speed and documentation. It is Pre 1.0, extreme beta mode with lots of breaking changes.
Generally speaking I think it is the right trade off for now. Purely inferring from Andrew and the Zig's team online character as I don't know them in person, I think they do care a lot of DX, things like compiling speed and tools. So I think once 1.0 come I won't be surprised if it will have extremely good documentation as well.
And I would argue, writing good, simple, clear, detailed documentation is actually harder than writing code itself.
I've written many APIs. Never have I got it right without first writing lots of tests, finding the rough corners, improving it... and so on. Writing documentation after that is absolutely mandatory for the end result to be a high quality API. As you write how it is meant to work, you will definitely find things that don't really make sense, or that should not be as hard ( I think this post shows just such an API that hasn't gone through this process ). IMHO documentation is NOT optional. The implementation is NOT how you mean for the API to be used.
My quick skim of Wikipedia may not be telling the complete story, but it says the initial release was 9 years ago (February 2016). After nearly a decade, I would hope that things would be out of "extreme beta mode," but I guess this isn't the case?
There is no benchmark. As a species, we don't even know know what a good programming language is, let alone how to reliably develop one. This stuff takes time, and we're all learning it together.
I like to compare this to real world cathedral building. There are some cathedrals that are literally taking centuries to build! It's OK if the important, but difficult thing takes a long time to build.
On the one hand, I totally get that pre 1.0 is the wild west (somewhat) and should be. The team is right in jealously guarding their ability to make changes.
That said, others have pointed out that writing documentation and tests helps improve quality quite a bit, and in this case it would also increase usability. I think I'd agree with this stance, but there is no way I could make the statement that even most of the code I've written for public consumption had excellent documentation or examples. So I've got no leg to stand on there, just the armchair.
> And I would argue, writing good, simple, clear, detailed documentation is actually harder than writing code itself.
All the more reason why it must be done! A little silly but from my armchair maybe it's one of those "start with the interface you want and work backwards", but the problem is that approach can be at odds with mechanical sympathy and we know which side Zig lands on (and arguably should land on based on it's values).
Contributions to the Zig language or contributions to software using Zig (the latter is the one the post is about as I understand)?
If so, I believe Zig will stay within a niche. Lower entry barriers allow "script kiddies" to easily start withe language, and they eventually will become leading engineers. Only a few people tend to go straight for the highest practice without "playing around". IMHO the reason, why PHP got so popular (it was not good back then, just very very easy to start with).
> Contributions to the Zig language or contributions to software using Zig (the latter is the one the post is about as I understand)?
Yes.
I think a contributor that really wanted to help the ecosystem would start in the stdlib and then start moving outwards. Even if it was LLM-assisted, I think it could be high value.
IIRC Loris already has an engine for building websites with Zig, but making sure that every Zig library has docs (similar to rustdocs) might be a great start. It is incredibly useful to have a resource like rustdocs, both the tooling and the web sites that are easily browsable.
Again, maybe everyone in the Zig ecosystem just has amazing editor setups and massive brains, but I personally really like the ease of browsing rustdoc.
> If so, I believe Zig will stay within a niche. Lower entry barriers allow "script kiddies" to easily start withe language, and they eventually will become leading engineers. Only a few people tend to go straight for the highest practice without "playing around". IMHO the reason, why PHP got so popular (it was not good back then, just very very easy to start with).
I agree, but I'd add that the niche they're aiming for is systems programming, so they're probably fine :). The average hacker there is expecting C/C++ or to be near the metal, and I think Zig is a great fit there. They're likely not going to convince people who write Ruby, but it feels reasonable for C hackers.
Also I want to just be clear that I think Zig has a lot of motivating factors! They're doing amazing things like zig cc, unbelievably easy, "can't believe it's not butter" cross-compilation, their new explicit/managed I/O mechanism, explicit allocators as a default, comptime, better type ergonomics. It's a pretty impressive language.
> already has an engine for building websites with Zig, but making sure that every Zig library has docs
Tbh, this sort of auto-generated docs from source code is not all that useful, since you get that same information right in the IDE via the language server.
The important documentation part that's currently missing is how everything is supposed to work together in the stdlib, not the 'micro-documentation' of what a single type or function does. And for this sort of information it's currently indeed better to look at example code (e.g. the stdlib's testing code).
IMHO it's way too early for this type of high-level documentation, since things change all the time in the stdlib. Putting much work into documenting concepts that are discarded again anyway doesn't make much sense.
I think it is intentional. They don't want to attract low-commitment beginners while the language is heavily changing (and explicitly in beta). Such people will ask questions and ask for documentation but contribute nothing.
The key problem with Zig nowadays is how much of its community and adoption is driven by anti-Rust sentiment. As a result, while Rust puts beginner onboarding and documentation at the center of its culture, as opposed to the “C neckbeard”'s culture, Zig is going the other way around.
(Loris Cro being a key community figure isn't helping in any way, and it's a good remainder that if you don't clear up your community from bullies from the beginning, they will turn your entire community to a miserable place. And that's a shame because from what I've seen, Andrew Kelley seems to be a very cool guy in addition to being very smart).
Not only, the whole handmade movement puts me off.
It is the anti-intelectualism from Go culture, gone wild against C++, Rust, Swift, anything modern, or even tools, using game engines versus doing the whole computer from scratch for a game.
Zig is not really a handmade project, case in point both Andrew and I are blocked on social media by the two gods of the handmade movement (casey and john) and, according to their die hard fans, Andrew gave a talk at the last handmade conference that caused the community to split apart (the reality is a bit more complex than this, but Andrew's talk is certainly one that you wouldn't see at their new "better software" conference).
> The key problem with Zig nowadays is how much of its community and adoption is driven by anti-Rust sentiment. As a result, while Rust puts beginner onboarding and documentation at the center of its culture, as opposed to the “C neckbeard”'s culture, Zig is going the other way around.
Maybe, or maybe the fact that Zig is a small independent project with limited resources has also something to do with it, and this kind of shaming says less about Zig than you'd think.
When I first joined the Zig project, Zig was still using the bootstrap compiler written in C++ that would not free memory (it took more than 4GB to compile the Zig compiler). Some people at the time were asking us to prioritize work on the package manager but Andrew rightfully wanted to prioritize rewriting the compiler instead. In hindsight this was the obviously right decision: a package manager implies that one can very easily add an order of magnitude more code to their project, stressing the performance of the compiler. If we had not prioritized core infrastructure over giving people what they wanted faster, today we would have people complaining that adding a single dependency to their project makes the build impossible to complete.
The Zig project has a huge scope and we are a small independent organization. This makes us extremely nimble and efficient, but it does mean that we need to do things in the order that makes the most sense for the project, not for what the public wants.
The fact that we develop in the open doesn't mean that the language is ready yet.
People that already have the required domain knowledge (and who have a tolerance for breaking changes) will have the opportunity to be early adopters if they wish to do so, others will have to wait for Zig to become more mature. And we do make this clear in releases and all forms of public communication.
We have gone a long way since the bootstrap compiler days, but we are still missing key infrastructure:
- we have a x86_64 custom backend but aarch64 is not complete yet
- incremental compilation is showing that we can get instant rebuilds of large projects, but it has missing features and it doesn't work on all platforms yet
- we need native fuzzing since AFL keeps regressing everytime a new version of LLVM comes out
- for the longest time we haven't had a strong I/O story, now we're finally working on it
The time for paving the road for a new generation of programmers will come (it's in the ZSF mission statement btw), but first we need to finish the plumbing.
> […] Zig was still using the bootstrap compiler written in C++ that would not free memory […]
That sounds strange. Modern C++ requires very little manual memory management, at least when you're writing something high-level like a compiler. C++11 had been out for years when development on Zig started. Were they writing C++ old-school as C-with-classes and malloc() everywhere? Why not use a more appropriate language for the first prototype of a compiler for a brand new language?
IIRC, it was a performance thing, and it's not an uncommon pattern in CLI tools. Freeing memory can actually cost you performance, so why not just let the OS clean up for you at exit(2)?
There is a cost to writing documentation - it takes time, which could be used to improve Zig in other areas. For code that is work-in-progress, it can make sense to not document until things are more settled.
Of course documentation is good. But if you have to prioritize either a new feature, or a critical bugfix, or documentation, you often can't have it all
I tend to actually disagree with this attitude, because I see writing documentation as really effective "rubber-ducking". If it's hard and time-consuming to properly document, it's probably hard to use, so extra effort should be spent to actually justify the design, not least to yourself in 6 months. If you can't justify it, it's probably wrong.
This really struck a chord with me. Writing documentation is an act of explaining something to others. Explaining something to others is a great way to test your own understanding. If it's hard to explain to someone else, then maybe it's the wrong design.
If you don't through that exercise, you're much more likely to build confusing, difficult-to-use APIs.
Because the language is not stable at this point and hasn't reached 1.0?
Are you saying one should never make anything half finished available to the public? This post proves why it is valuable to do so, they are getting valuable feedback and a discussion on hacker news for free.
There's an alternative of being much more up front about the status. For example the project page doesn't really say it's unstable/experimental. It only says "Zig has not yet reached v1.0" on the "getting started" page, which doesn't really mean that much - for example Putty is still at 0.83 after 26 years.
If the project invites me to use it "for maintaining robust, optimal and reusable software." without putting "super unstable, we don't even care about docs" on the same page... that's also saying something.
Your argument implies that good documentation is not an improvement, which of course is wrong. It also belongs to the task of improving code. Why would you move away after half-assing the API, when you can add the docs and whole-ass it instead?
i find that zig is too oriented at doling out directives for what not to do instead of just collecting and teaching variants of how and what to do. the lack of documentation on this interface is a sore case in point.
That's ok. "It's an unstable, experimental, early version." is a valid explanation. GP put the lack of docs and the new interface together, which... isn't an excuse.
I'm not a Zig developer, but I imagine one reason why the Zig documentation is so spartan is because the language is still young and constantly evolving. It's really hard to devote the time and energy to writing documentation when you know that what you've written will just be wrong at some uncertain point in the future.
The Zig's language is really good, but the standard library is really a big work in progress, constantly shifting, missing a lot of bits, overly abstracted at some places and too low level at other places.
I would say just stay away from the standard library for now and use your OS API, unless you're willing to be a beta tester.
I have never understood libraries or imterfaces that want me to allocate buffers for their type. I can't parse them (no need for the lib then) or write to them (would probably break the exchange).
The weird interface of go is probably due the fact that some interfaces can be used to extemd the writer like the hijacker interface (ResponseWriter.(http.Hijacker)) and the request object is used multiple times with different middlewares interacting with it.
In short: request does not need to be extended, but the response can be an websocket, an wrapped tcp connection or something else.
> I have never understood libraries or imterfaces that want me to allocate buffers for their type.
That doesn't seem that odd to me. It's a trade off: more flexibility, but more manual work. Maybe I have a buffer that I've allocated that I'm not using anymore (say I have a buffer pool) and want to use it again. If the type allocates its own behind the scenes, I can't do that. Or maybe I'm working in an environment where I need to statically allocate all of my resources up-front, and can't allocate later.
The big downside is that if 90% of people are just going to allocate a buffer and pass it in, it sucks that 90% of people need to do more work and understand more minutiae when only 10% of the people actually need to. The holy grail is to give lots of flexibility, but make the simple/common case easy.
A simple improvement to this interface might be to allow the caller to pass a zero-length buffer (or Zig's version of null), and then the type will allocate its own buffer. Of course, there's still a documentation burden so people know they can do that. Another option could be to have second constructor function that takes no buffer arguments at all, which allocates the buffers and passes them to the fully-flexible constructor function.
Only if use after free story actually gets fixed, and not by repurposing what has already existed in the C and C++ ecosystems for the last 30 years, like PurifyPlus or VC++ debug allocator.
If you mean running clang-tidy as a separate build step or ASAN in a different category than other soundness checks?
Compute is getting tight, lots of trends, the age of C++ is winding down gracefully. The age of Zig is emerging delibetately, and the stuff in the middle will end up in the same historical trash bin as everything else in the Altman Era: the misfortunes of losing sight of the technology.
I mean those and other ones, we already have enough unsafe languages as it is.
The age of C++ is going great, despite all its warts and unsafety, thanks to compiler frameworks like GCC and LLVM, games industry, GPGPU and Khronos APIs.
Even if C++ loses everywhere else, it has enough industry mindshare to keep being relevant.
Same applies to C, in the context of UNIX clones, POSIX, Khronos, embedded.
Being like Modula-2 or Object Pascal in safety, in C like syntax, isn't enough.
Haskell makes guarantees. Modern C++ makes predictions to within a quantifiable epsilon.
Rust makes false promises in practical situations. It invented a notion of safety that is neither well posed, nor particularly useful, nor compatible with ergonomic and efficient computing.
It's speciality is marketing and we already know the bounding box on its impact or relevance. "Vibe coding" will be a more colorful and better remembered mile marker of this lousy decade in computers than Rust, which will be an obscurity in an appendix in 100 years.
There is almost nothing accurate about this comment.
"Makes predictions to within a quantifiable epsilon"? What in the world do you mean? The industry experience with C++ is that it is extremely difficult (i.e., expensive) to get right, and C++20 or newer does not change anything about that. Whatever "epsilon" you are talking about here surely has to be very large for a number bearing that sobriquet.
As for the mindless anti-Rust slander... I'm not sure it's worth addressing, because it reflects a complete lack of the faintest idea about what it actually does, or what problem it solves. Let me just say there's a reason the Rust community is rife with highly competent C++ refugees.
> "Vibe coding" will be a more colorful and better remembered mile marker of this lousy decade in computers than Rust, which will be an obscurity in an appendix in 100 years.
I doubt it.
I'm teaching a course on C this fall. As textbook I've chosen "Modern C" by Jens Gustedt (updated for C23).
I'm asked by students "Why don't you choose K&R like everyone else?"
And while the book is from 1978 (ANSI C edition in 1988), and something I've read joyously more than once, I'm reminded of how decades of C programmers have been doing things "the old way" because that's how they're taught. As a result, the world is made of old C programs.
With this momentum of religiously rewriting things in Rust we see in the last few years (how many other languages have rewritten OpenSSL and the GNU coreutils?), the amount of things we depend on that was incidentally rewritten in Rust grows significantly.
Hopefully people won't be writing Rust in 100 years. Since 100 years ago mathematicians were programming mechanical calculators and analog computers, and today kids are making games. But I bet you a whole lot of infrastructure still runs Rust.
In fact, anything that is convenient to Vibe code in the coming years will drown out other languages by volume. Rust ain't so bad for vibe coding.
There is a place to learn about history of computing, and that is where K&R C book belongs to.
Not only is the old way, this is from the age of dumb C compilers, not taking advantage of all stuff recent standards allow compiler writers to take to next level on optimizations, not always with expected results.
Maybe getting students to understand the ISO C draft is also an interesting exercise.
I hope in 100 years we're not using any of the languages available today. I do like Rust, and use it whenever it's appropriate, but it has its warts and sharp edges. Hopefully we'll come up with something better in the next century.
> Rust makes false promises in practical situations. It invented a notion of safety that is neither well posed, nor particularly useful, nor compatible with ergonomic and efficient computing.
Please stop. Rust's promise is very simple. You get safety without the tracing GC. It also gives you tools to implement your own safe abstraction on top of unsafe, but you are mostly on your own (miri, asan, and ubsan can still be used).
Neither Rust nor Ada nor Lean nor Haskell can guarantee there are no errors in their implementations.
Similarly, none of the listed languages can even try to show that a bad actor can't write bad code or design bad hardware in a way that maintains their promises. If you need that, you need to invent the Omniscient Oracle, not a program.
I hate this oft repeated Nirvana fallacy. Yes, Rust is offering you a car with seatbelts and airbags. It is not offering a car that guarantees immortality in the event of a universe collapse.
People state these things about Rust's own implementation (or one of the other gazillion safe langs) potentially not being safe all the time, but the difference to unsafe languages is, that once any bug is fixed, everyone profits from it being fixed in the implementation of Rust. Everyone who uses the language and updates to a newer version that is, which often goes without code changes or minimal changes for a project. Now compare that with unsafe languages. Every single project needs to "fix" the same kind of safety issues over and over again. The language implementation can do almost nothing, except change the language to disallow unsafe stuff, which is not done, because people like backwards compatibility too much.
> People state these things about Rust's own implementation (or one of the other gazillion safe langs) potentially not being safe all the time
Because it's technically true. The best source of true!
Sorry, meant to say the opposite of truth. Neither Rust nor Ada.Spark, which use LLVM as a backend, can prove via their implementation there are no errors in LLVM.
In the same way, I can't guarantee tomorrow I won't be killed by a rogue planet hitting Earth at 0.3c. So I should probably start gambling and doing coke, because we might be killed tomorrow.
> Every single project needs to "fix" the same kind of safety issues over and over again
I doubt that's the biggest problem. Each of the unsafe libraries in C/C++/Zig can be perfectly safe given invariants X and Y, respectively. What happens if you have two (or more) libraries with subtly non-compatible invariants? You get non-composable libraries. You end up with the reverse problem of the NPM world.
its bad because they are mixing what was supposed to just be execution boundaries into the overall runtime engine without making it explicit how to bridge between one and another.
I’m sorry, but any non-trivial Zig code gives me PTSD flashbacks of C. I don’t understand who Zig is targeting: with pervasive mutability, manual allocation, and a lack of proper sum types, it feels like a step back from languages such as Rust. If it is indeed a different way to write code, one that embraces default memory unsafety, why would I choose it over C, which has decades of work behind it?
I love Zig precisely because it is so similar to C. Honestly, if you don't like C, I can totally understand why you wouldn't like Zig. But I love C, and I love Zig.
Zig has become my go-to for projects where I would previously have reached for C, largely because Zig has such good compatibility with other C projects.
Rust, on the other hand, is a completely different beast. It is very different from C, and it is far more complicated. That makes it harder to justify using, whereas Zig is a very easy choice as an alternative to using C itself.
C is entirely as complicated as Rust, if your goal is to write correct software that doesn't crash all the time. It's only a syntactically simple language. Actually making anything interesting with it is _not_ simple.
I don't think a 100-line function signature is representative, but I will point out that the alternative is at least 100 lines of runtime checks instead. In both cases, what a nightmare.
Better default integer type casting, ability to choose between releaseSafe/releaseFast
And probably other things.
As for comparison to Rust, you do want very low level memory handling for writing databases as an example. It is extremely difficult to write low level libraries in Rust
I think the argument is that it is also extremely difficult to write low level libraries in Zig, just as it is in C. You will just only notice the difficulty at some later point after writing the code, potentially in production.
Did you write any Zig code yet? In terms of enforced correctness in the language (e.g. no integer promotion, no implicit 'dangerous' casts, null-safety, enforced error handling, etc...) and runtime safety (range-, nullptr-, integer-overflow-checks etc...), Zig is much closer to Rust than it is to C and C++.
It "just" doesn't solve static memory safety and some (admittedly important) temporal memory safety issues (aka "use-after-free"), but it still makes it much harder to accidentially trigger memory corruption as a side effect in most situations that C and C++ let slip through via a mix of compile errors and runtime checks (and you get ASAN/UBSAN automatically enabled in debug builds, a debug allocator which detects memory leaks and use-after-free for heap-allocations (unfortunately not for stack allocations), and proper runtime stack traces - things that many C/C++ toolchains are still missing or don't enable by default).
There is still one notable issue: returning a reference to stack memory from a function - this is something that many unexperienced Zig programmers seem to stumble into, especially since Zig's slice syntax looks so 'innocent' (slices look too similar to arrays, but arrays are values, while slices are references - e.g. 'fat pointers') - and which IMHO needs some sort of solution (either a compile time error via watertight escape analysis, or at least some sort runtime check which panics when trying to access 'stale' data on the stack) - and maybe giving slices their own distinct syntax that doesn't overlap with arrays might also help a bit.
I mean, there's no question that Zig, also in its current state, is vast improvement over C or even C++ - for the "small stuff". It is much more pleasant to use.
But there is still the "big stuff" - the things that have a fundamental, architectural impact. Things like: Will my program be multithreaded? Will I have many systems that interact? Will my program be maximally memory-efficient? Do I have the capacity (or money) to ensure correctness if I say "yes" to any of that?
The most important consideration in any software project is managing architectural complexity. Zig is better, yes, but not a paradigm shift. If you say "yes" to any of the above, you are in the same world of pain (or expenses) as you would be in C or C++. This is the reason that Rust is interesting: It makes things feasible/cheap that were previously very hard/expensive, at a fundamental level.
But it doesn't have to be and it shouldn't. Rust also isn't a paradigm shift, it "just" solved static memory safety (admittedly a great engineering feat) but other languages solved memory safety too decades ago, just with more of it happening at runtime.
But in many other areas Rust copied too many bad ideas from C++ (and many of those "other things" Zig already does much better than Rust - but different people will have vastly different opinions about whether one solution is actually better than another - so discussions about those features usually run in circles).
There shouldn't be a single language that solves all problems - this will just result in stagnation, it's much better to have many languages to pick from - and even if they just differ in personal opinions of the language author of how to do things or entirely subjective 'syntax sugar' features. Competition is good, monocultures are bad.
> The most important consideration in any software project is managing architectural complexity
No language will help you managing "architectural complexity" in any meaningful way except by imposing tons of arbitrary restrictions which then get in the way for smaller projects that don't need much "architecture". We have plenty of "Enterprise-scale languages" already (Java, C#, Rust, C++, ...), what we need is more languages for small teams that don't get in the way of just getting shit done, since the really interesting and innovative stuff doesn't happen in enterprise environments but in small 'basement and bedroom teams' :)
You put "just" in scare quotes, but that word does a lot of heavy lifting there. Static memory safety is an extremely useful thing, because it enables you to do things competent programmers would never dare in C, C++, or Zig. Things like borrowing data from one thread's stack in another thread, or returning anything but `std::string` from a function. These things were simply not feasible before without a huge bulky runtime and GC.
Keep in mind that Rust's definition of "memory safety" covers much more than just use-after-free, the most important being thread safety. It is a blanket guarantee of no undefined behavior in any code that doesn't contain the word `unsafe`. Undefined behavior, including data race conditions, is a major time sink in all non-hobby C or C++ projects.
What bad ideas from C++ did Rust copy, in your opinion? I'm really not sure what you mean. Smart pointers? RAII?
There are plenty of languages that enable quick iteration, prototyping, or "just getting shit done". If that's what you need, why not use them? I'm personally more concerned about the finished product.
...I don't use them as "scare quotes", it's more like in "can't you just..." - e.g. something that looks simple from the outside but is hard to do / complex on the inside - e.g. I do recognize the work that went into Rust's memory safety, but I question the effort that went into it compared to more traditional memory safety methods, especially when looking at the restrictions that Rust imposed on the programmer - it's a pretty hefty tradeoff (IMHO of course).
> What bad ideas from C++ did Rust copy, in your opinion?
Mainly doing things in the stdlib that should be built into the language (e.g. Option, Result, Box, Cell, RefCell, Arc, and probably a dozen other types and traits...), resulting in what I call 'bird droppings syntax' of too many chained function calls to get to the thing you actually want (.unwrap, .into_iter, .iter, .iter_mut, .as_ref, .as_mut, .expect, .unwrap_or_else, .unwrap_or_default, .ok, .ok_or_else, .and_then, .or_else ... like, wtf?). The absurd amount of `::` and `<>` in typical Rust code. The missing separation line between stdlib and language (like for-loops using the Iterator trait, or more obviously: operator overloading). The stdlib doing memory allocations behind your back through a global allocator - which we know from C++ do be a really bad idea for decades already... etc etc... I think most Rust programmers are blind towards those issues the same way that C++ programmers are blind towards C++ issues (which is another thing both language ecosystems have in common though).
I mean, there's no winning here. Either the language is too complex and does too many things, or it's not complex enough and relegates fundamental things to the standard library.
I don't thing there is any substantial difference between `Option<Thing>` and `@Nullable Thing` or `Thing | null`, I don't think there's anything wrong with choosing `::` over `.` for namespace resolution (it means you can have local variables with the same name as a module), and you have to have some way to declare generic parameters.
Rust generally does not allocate behind your back, but custom allocators is a work in progress. The reason it takes time is precisely that they want to avoid the mistakes of C++. A couple of mistakes were already avoided - for example, async/await in Rust does not allocate behind your back, while C++ coroutines do.
> I think the argument is that it is also extremely difficult to write low level libraries in Zig, just as it is in C.
This has been not my experience at all in the ~6 years I've been writing Zig. I started having very little experience writing C (<1000, lines all written while in university) and since day 1 Zig has been a tremendous improvement over it, even back when it was at version 0.4.0.
I'm informed by having shipped a lot of C++ code in my time, which has taught me a lot about actually delivering stable software. Being better than C is a very low bar here.
Zig is a systems programming language. I think that's probably who it's targeting.
People do systems programming in rust, but that's not really what most of the community is doing. And it's DEFINITELY not what the standard library is designed for.
Web servers, games, and applications, that sort of thing.
Some people definitely do systems programming in, but it's a minority. The std library is not set up for it at all, you need something like rustix, but even that results in very unidiomatic ("unsafe") rust code.
In Zig it's all in the std library by default. Because it's a systems programming language, first and foremost.
Actually I was also under OPs impression... can you tell me few specific problems with using rust for systems programming? BTW, I have only ever done something that resembles systems programming in C.
Because Rust's standard library doesn't provide memory mapping you will need to use platform specific APIs.
In Zig it's exactly the same except that they decided to provide the POSIX platform specific APIs, which if you're using a POSIX system is I guess useful and otherwise it's dead weight.
It's a choice. I don't think it's a good choice but it's a choice.
I think you are moving the goal posts. You use the `memmap2` crate, or the `libc` crate if you want to be reckless about it. The question was how the standard library gets in your way, not whether it includes everything you need.
And I don't think that including every feature of every possible OS is a sensible position to have for a standard library. Or would you argue that it should also include things like `CreateWindowExW()`?
If all you use is the Rust standard library, you can be reasonably sure that your program works on all platforms, and memory mapping is something that is highly platform specific, even among POSIX-likes. I would not like to attempt designing a singular cross-platform API for it that has to be maintained in perpetuity.
(There are a few OS-specific APIs in the Rust standard library, mostly because they are required anyway for things like I/O and process management. But the limit has to be set somewhere.)
Your question might hint at a questionable presumption. So let me answer your question with a question - Does one have to memory map in Rust? Perhaps there are alternatives available in Rust, that you are not considering.
The new I/O interface makes printing a simple “Hello, world!” more complicated, but once you get used to it, the design is actually very clean, versatile, and future-proof.
Since 0.15, though, I feel too dumb for Zig’s ArrayList.
Is it future-proof though? Last I saw, it relied on some yet-to-be-determined design for compiling async variants of everything that uses IO, and it was still unclear whether it was possible at all to support dynamic dispatch.
My info could be outdated - I don't follow Zig very closely, but I am curious.
The Reader/Writer changes are perfectly compatible with the upcoming async I/O stuff and you won't need to change any code that just deals with streams.
No promises about potential future changes though :^)
Author here.
I finally got it working. I had to flush both the encrypted writer and then the stream writer. There was also some issues with reading. Streaming works, but it'll always return 0 on the first read because Writer.Fixed doesn't implement sendFile, and thus after the first call, it internally switches from streaming mode to reading mode (1) and then things magically work.
Currently trying to get compression re-enabled in my websocket library.
(1) https://github.com/ziglang/zig/blob/47a2f2ddae9cc47ff6df7a71...
Ha, "Don't Forget to Flush" https://www.youtube.com/watch?v=f30PceqQWko
I'm not a Zig PM but the first obvious fix for the issues the OP wrote about is to write better documentation, including usage examples (the more the better, almost to a fault). Also doubles as a good time to reflect on whether the user is having to do too much.
If the tradeoff was absolute performance/avoiding introducing load-bearing performance-lowering abstraction I think that goal was achieved, but DX may have gone out the window.
You’re not familiar with Zig’s culture, I guess. Complain about the lack of documentation and be prepared for the flood of “just read the stdlib code” helpful comments by pretty much everyone who writes Zig right now. Because most APIs are just as hard to use as in this post (check things like HTTP and even basic file system operations) only the strongest survive.
I do not think this is a viable excuse any more.
I am just editing docs now that Claude Code writes for me. I am fanatic about developer docs (and I guess an exception as I love writing them) but with a set of concise instructions for CC and some writing style examples I get 90% there, sometimes 99%.
If you believe you don't have time for the last 1--10% you should not be in charge of writing any API used by anyone but yourself. Just my two c.
Ai is great at block comments, there is no excuse. Add to that a small anotated usage example written by a human and this whole post would have not existed.
Lack of docs also cripple AI from understanding, so future adoption becomes even more bleak.
If an api or library developer didnt bother doing even bare minimum docs, my confidence in the library drops aswell.
Did they skip testing aswell? Ran the happy path for a day and called it good?
This post sour my interest in zig. Its now obvious to me now why rust took much of its market.
Zig is just getting started and came way after rust.
I like Zig, but if we look at the numbers, the difference is probably more to do with funding that anything:
Zig (programming language) - First appeared 8 February 2016; 9 years ago
Rust (programming language) - First appeared January 19, 2012; 13 years ago
Also, Zig at this point isn't really a brand new language anymore. I have comments on their issues dating back to 2018, so it's been a very active language since at least then.
Those are not comparable dates. The Zig "first appeared" date is a few months into development by Andrew in his spare time. The Rust "first appeared" date is after 3 years of development by Graydon in his spare time, followed by 3 years of development by a Mozilla-sponsored team of engineers.
I don't want to read ai slop comments. If you cant be bothered writing docs, I cant be bothered learning to use your library.
That would hurt adoption. I understand things move fast but if you want people to make the switch other than hello world, it has to be at a minimum cosy. Sending them to hell and find your way out isn't a good move long term.
I tried Zig a couple of times and I got that feeling: very powerful and clever language but not really for me, I don't have the headspace, sorry. I need something I can debug after an 8 hours dayjob, a commute and having put the kids to bed. It better be inviting & fun! (Hi, C).
Yeah, thinking about this attitude positively, maybe it’s a feature — if only hard core people can comfortably figure it out, you get higher quality contributions?
Not trying to imply that’s an explicit goal (probably instead just a resource problem), but an observation
I think it is a trade off for between zig's development speed and documentation. It is Pre 1.0, extreme beta mode with lots of breaking changes.
Generally speaking I think it is the right trade off for now. Purely inferring from Andrew and the Zig's team online character as I don't know them in person, I think they do care a lot of DX, things like compiling speed and tools. So I think once 1.0 come I won't be surprised if it will have extremely good documentation as well.
And I would argue, writing good, simple, clear, detailed documentation is actually harder than writing code itself.
I've written many APIs. Never have I got it right without first writing lots of tests, finding the rough corners, improving it... and so on. Writing documentation after that is absolutely mandatory for the end result to be a high quality API. As you write how it is meant to work, you will definitely find things that don't really make sense, or that should not be as hard ( I think this post shows just such an API that hasn't gone through this process ). IMHO documentation is NOT optional. The implementation is NOT how you mean for the API to be used.
My quick skim of Wikipedia may not be telling the complete story, but it says the initial release was 9 years ago (February 2016). After nearly a decade, I would hope that things would be out of "extreme beta mode," but I guess this isn't the case?
For most of its time it is simply a single person and part time project. Even to this day the team is nowhere near Rust or Go's resources.
Huh, I actually expected there to be a bigger team working on it. In that case: I'm really impressed.
What's the benchmark for how long something can be pre-1.0? Seems like a nonsense argument.
It's the combination of pre-1.0 and having rapid development speed that is being questioned here. And it's a good question, not nonsense.
If you keep up the development pace you're going to approach stability. Unless you're in a manic spiral of rewrites.
Something can be pre-1.0 as long as there are no stability guarantees.
There is no benchmark. As a species, we don't even know know what a good programming language is, let alone how to reliably develop one. This stuff takes time, and we're all learning it together.
I like to compare this to real world cathedral building. There are some cathedrals that are literally taking centuries to build! It's OK if the important, but difficult thing takes a long time to build.
Cathedrals are the opposite of extreme beta mode with lots of breaking changes.
Same as Rust being almost a decade old when the first 1.0 was published.
Making a programming language from scratch is a long endeavor when it's a one man project.
On the one hand, I totally get that pre 1.0 is the wild west (somewhat) and should be. The team is right in jealously guarding their ability to make changes.
That said, others have pointed out that writing documentation and tests helps improve quality quite a bit, and in this case it would also increase usability. I think I'd agree with this stance, but there is no way I could make the statement that even most of the code I've written for public consumption had excellent documentation or examples. So I've got no leg to stand on there, just the armchair.
> And I would argue, writing good, simple, clear, detailed documentation is actually harder than writing code itself.
All the more reason why it must be done! A little silly but from my armchair maybe it's one of those "start with the interface you want and work backwards", but the problem is that approach can be at odds with mechanical sympathy and we know which side Zig lands on (and arguably should land on based on it's values).
Unstable APIs are a good example of something that's extremely valuable early on.
They unarguably cause confusion for everyone as they change.
But it lets you choose the right abstractions that are going to stick for decades.
If you're going to make a python2 -> python3 transition in your language, make sure it's X0 -> X1.
Contributions to the Zig language or contributions to software using Zig (the latter is the one the post is about as I understand)?
If so, I believe Zig will stay within a niche. Lower entry barriers allow "script kiddies" to easily start withe language, and they eventually will become leading engineers. Only a few people tend to go straight for the highest practice without "playing around". IMHO the reason, why PHP got so popular (it was not good back then, just very very easy to start with).
> Contributions to the Zig language or contributions to software using Zig (the latter is the one the post is about as I understand)?
Yes.
I think a contributor that really wanted to help the ecosystem would start in the stdlib and then start moving outwards. Even if it was LLM-assisted, I think it could be high value.
IIRC Loris already has an engine for building websites with Zig, but making sure that every Zig library has docs (similar to rustdocs) might be a great start. It is incredibly useful to have a resource like rustdocs, both the tooling and the web sites that are easily browsable.
Again, maybe everyone in the Zig ecosystem just has amazing editor setups and massive brains, but I personally really like the ease of browsing rustdoc.
> If so, I believe Zig will stay within a niche. Lower entry barriers allow "script kiddies" to easily start withe language, and they eventually will become leading engineers. Only a few people tend to go straight for the highest practice without "playing around". IMHO the reason, why PHP got so popular (it was not good back then, just very very easy to start with).
I agree, but I'd add that the niche they're aiming for is systems programming, so they're probably fine :). The average hacker there is expecting C/C++ or to be near the metal, and I think Zig is a great fit there. They're likely not going to convince people who write Ruby, but it feels reasonable for C hackers.
Also I want to just be clear that I think Zig has a lot of motivating factors! They're doing amazing things like zig cc, unbelievably easy, "can't believe it's not butter" cross-compilation, their new explicit/managed I/O mechanism, explicit allocators as a default, comptime, better type ergonomics. It's a pretty impressive language.
> already has an engine for building websites with Zig, but making sure that every Zig library has docs
Tbh, this sort of auto-generated docs from source code is not all that useful, since you get that same information right in the IDE via the language server.
The important documentation part that's currently missing is how everything is supposed to work together in the stdlib, not the 'micro-documentation' of what a single type or function does. And for this sort of information it's currently indeed better to look at example code (e.g. the stdlib's testing code).
IMHO it's way too early for this type of high-level documentation, since things change all the time in the stdlib. Putting much work into documenting concepts that are discarded again anyway doesn't make much sense.
I think it is intentional. They don't want to attract low-commitment beginners while the language is heavily changing (and explicitly in beta). Such people will ask questions and ask for documentation but contribute nothing.
The key problem with Zig nowadays is how much of its community and adoption is driven by anti-Rust sentiment. As a result, while Rust puts beginner onboarding and documentation at the center of its culture, as opposed to the “C neckbeard”'s culture, Zig is going the other way around.
(Loris Cro being a key community figure isn't helping in any way, and it's a good remainder that if you don't clear up your community from bullies from the beginning, they will turn your entire community to a miserable place. And that's a shame because from what I've seen, Andrew Kelley seems to be a very cool guy in addition to being very smart).
Not only, the whole handmade movement puts me off.
It is the anti-intelectualism from Go culture, gone wild against C++, Rust, Swift, anything modern, or even tools, using game engines versus doing the whole computer from scratch for a game.
Zig is not really a handmade project, case in point both Andrew and I are blocked on social media by the two gods of the handmade movement (casey and john) and, according to their die hard fans, Andrew gave a talk at the last handmade conference that caused the community to split apart (the reality is a bit more complex than this, but Andrew's talk is certainly one that you wouldn't see at their new "better software" conference).
Andrew's talk is here (second event after the two people chatting while sitting on chairs): https://handmadecities.com/media/seattle-2024/hms-day-one/
Here you can see a particularly funny (but also sad) reaction by one of these people https://drive.proton.me/urls/MB1EB4EF34#YZdvmAvBFp1C
> using game engines versus doing the whole computer from scratch for a game
That said you are doing yourself a disservice if you think that not using an engine to make a game is a form of "anti-intellectualism".
> The key problem with Zig nowadays is how much of its community and adoption is driven by anti-Rust sentiment. As a result, while Rust puts beginner onboarding and documentation at the center of its culture, as opposed to the “C neckbeard”'s culture, Zig is going the other way around.
Maybe, or maybe the fact that Zig is a small independent project with limited resources has also something to do with it, and this kind of shaming says less about Zig than you'd think.
When I first joined the Zig project, Zig was still using the bootstrap compiler written in C++ that would not free memory (it took more than 4GB to compile the Zig compiler). Some people at the time were asking us to prioritize work on the package manager but Andrew rightfully wanted to prioritize rewriting the compiler instead. In hindsight this was the obviously right decision: a package manager implies that one can very easily add an order of magnitude more code to their project, stressing the performance of the compiler. If we had not prioritized core infrastructure over giving people what they wanted faster, today we would have people complaining that adding a single dependency to their project makes the build impossible to complete.
The Zig project has a huge scope and we are a small independent organization. This makes us extremely nimble and efficient, but it does mean that we need to do things in the order that makes the most sense for the project, not for what the public wants.
The fact that we develop in the open doesn't mean that the language is ready yet.
People that already have the required domain knowledge (and who have a tolerance for breaking changes) will have the opportunity to be early adopters if they wish to do so, others will have to wait for Zig to become more mature. And we do make this clear in releases and all forms of public communication.
We have gone a long way since the bootstrap compiler days, but we are still missing key infrastructure:
- we have a x86_64 custom backend but aarch64 is not complete yet - incremental compilation is showing that we can get instant rebuilds of large projects, but it has missing features and it doesn't work on all platforms yet - we need native fuzzing since AFL keeps regressing everytime a new version of LLVM comes out - for the longest time we haven't had a strong I/O story, now we're finally working on it
The time for paving the road for a new generation of programmers will come (it's in the ZSF mission statement btw), but first we need to finish the plumbing.
> […] Zig was still using the bootstrap compiler written in C++ that would not free memory […]
That sounds strange. Modern C++ requires very little manual memory management, at least when you're writing something high-level like a compiler. C++11 had been out for years when development on Zig started. Were they writing C++ old-school as C-with-classes and malloc() everywhere? Why not use a more appropriate language for the first prototype of a compiler for a brand new language?
IIRC, it was a performance thing, and it's not an uncommon pattern in CLI tools. Freeing memory can actually cost you performance, so why not just let the OS clean up for you at exit(2)?
There is a cost to writing documentation - it takes time, which could be used to improve Zig in other areas. For code that is work-in-progress, it can make sense to not document until things are more settled.
Of course documentation is good. But if you have to prioritize either a new feature, or a critical bugfix, or documentation, you often can't have it all
I tend to actually disagree with this attitude, because I see writing documentation as really effective "rubber-ducking". If it's hard and time-consuming to properly document, it's probably hard to use, so extra effort should be spent to actually justify the design, not least to yourself in 6 months. If you can't justify it, it's probably wrong.
This really struck a chord with me. Writing documentation is an act of explaining something to others. Explaining something to others is a great way to test your own understanding. If it's hard to explain to someone else, then maybe it's the wrong design.
If you don't through that exercise, you're much more likely to build confusing, difficult-to-use APIs.
100%.
Similar to how TDD forces you to first figure out the API of your code due to the test code being its first client.
This is a good point, but one could make a point for the usefulness of documentation in “thinking like a user” which is a valuable exercise.
I do very much prefer moving fast though, so I get it, docs-later is obviously a very valid way of doing things.
If someone is excited about Zig and wanted to make a difference I guess it’s now obvious where they could have outsized impact!
Docs later is an okay approach, when you build something in a closed environment, where the only users know the code inside out.
But when working in open-source and your goal is to have people adopt your software, then it's a bad point and a lazy excuse.
Then they should not have relead the new api at all. Why release half finnished library.
Zig as a whole is half-finished, should it be kept under wraps until it is ready?
There's a reason for the 0.x version number, if you can't live with breaking changes, don't use Zig yet. It's as simple as that.
Because the language is not stable at this point and hasn't reached 1.0?
Are you saying one should never make anything half finished available to the public? This post proves why it is valuable to do so, they are getting valuable feedback and a discussion on hacker news for free.
There's an alternative of being much more up front about the status. For example the project page doesn't really say it's unstable/experimental. It only says "Zig has not yet reached v1.0" on the "getting started" page, which doesn't really mean that much - for example Putty is still at 0.83 after 26 years.
If the project invites me to use it "for maintaining robust, optimal and reusable software." without putting "super unstable, we don't even care about docs" on the same page... that's also saying something.
Your argument implies that good documentation is not an improvement, which of course is wrong. It also belongs to the task of improving code. Why would you move away after half-assing the API, when you can add the docs and whole-ass it instead?
Very good point
Na. It’s flat out laziness. Don’t make excuses. Either write docs or stop worrying code.
Writing good docs/examples takes a lot of effort. It would be a waste considering the amount of churn that happens in zig at this point.
Yes. For now, that effort is better spent writing clear test cases that can serve to illustrate the intended usage.
While tests aren’t quite as good documentation as actual documentation, they are guaranteed to not be out of date.
i find that zig is too oriented at doling out directives for what not to do instead of just collecting and teaching variants of how and what to do. the lack of documentation on this interface is a sore case in point.
You can’t expect documentation this early. The new interface was just released.
In serious codebases docs are not an afterthought. There's lots of places where you're expected to add both a new interface and docs together.
It's pre-1.0 beta. Nothing has been 'released' yet .
That's ok. "It's an unstable, experimental, early version." is a valid explanation. GP put the lack of docs and the new interface together, which... isn't an excuse.
So zig isn’t a serious language. It’s just some trash apis thrown together.
It's a serious language, it's just not a stable language yet.
I'm not a Zig developer, but I imagine one reason why the Zig documentation is so spartan is because the language is still young and constantly evolving. It's really hard to devote the time and energy to writing documentation when you know that what you've written will just be wrong at some uncertain point in the future.
The Zig's language is really good, but the standard library is really a big work in progress, constantly shifting, missing a lot of bits, overly abstracted at some places and too low level at other places.
I would say just stay away from the standard library for now and use your OS API, unless you're willing to be a beta tester.
I have never understood libraries or imterfaces that want me to allocate buffers for their type. I can't parse them (no need for the lib then) or write to them (would probably break the exchange).
The weird interface of go is probably due the fact that some interfaces can be used to extemd the writer like the hijacker interface (ResponseWriter.(http.Hijacker)) and the request object is used multiple times with different middlewares interacting with it. In short: request does not need to be extended, but the response can be an websocket, an wrapped tcp connection or something else.
> I have never understood libraries or imterfaces that want me to allocate buffers for their type.
That doesn't seem that odd to me. It's a trade off: more flexibility, but more manual work. Maybe I have a buffer that I've allocated that I'm not using anymore (say I have a buffer pool) and want to use it again. If the type allocates its own behind the scenes, I can't do that. Or maybe I'm working in an environment where I need to statically allocate all of my resources up-front, and can't allocate later.
The big downside is that if 90% of people are just going to allocate a buffer and pass it in, it sucks that 90% of people need to do more work and understand more minutiae when only 10% of the people actually need to. The holy grail is to give lots of flexibility, but make the simple/common case easy.
A simple improvement to this interface might be to allow the caller to pass a zero-length buffer (or Zig's version of null), and then the type will allocate its own buffer. Of course, there's still a documentation burden so people know they can do that. Another option could be to have second constructor function that takes no buffer arguments at all, which allocates the buffers and passes them to the fully-flexible constructor function.
Isn’t the whole point of an external buffer that the function won’t need to allocate?
It's just a different convention like radians and degrees.
You can lift/unlift in or out of arbitrary IO, in some languages one direction is called a mock, in other languages the opposite is called unsafeFoo.
Andrew Kelley independently rediscovered on a live stream 30 years of the best minds in Haskell writing papers.
So the future is Zig. He got there first.
Only if use after free story actually gets fixed, and not by repurposing what has already existed in the C and C++ ecosystems for the last 30 years, like PurifyPlus or VC++ debug allocator.
If you mean running clang-tidy as a separate build step or ASAN in a different category than other soundness checks?
Compute is getting tight, lots of trends, the age of C++ is winding down gracefully. The age of Zig is emerging delibetately, and the stuff in the middle will end up in the same historical trash bin as everything else in the Altman Era: the misfortunes of losing sight of the technology.
I mean those and other ones, we already have enough unsafe languages as it is.
The age of C++ is going great, despite all its warts and unsafety, thanks to compiler frameworks like GCC and LLVM, games industry, GPGPU and Khronos APIs.
Even if C++ loses everywhere else, it has enough industry mindshare to keep being relevant.
Same applies to C, in the context of UNIX clones, POSIX, Khronos, embedded.
Being like Modula-2 or Object Pascal in safety, in C like syntax, isn't enough.
Haskell makes guarantees. Modern C++ makes predictions to within a quantifiable epsilon.
Rust makes false promises in practical situations. It invented a notion of safety that is neither well posed, nor particularly useful, nor compatible with ergonomic and efficient computing.
It's speciality is marketing and we already know the bounding box on its impact or relevance. "Vibe coding" will be a more colorful and better remembered mile marker of this lousy decade in computers than Rust, which will be an obscurity in an appendix in 100 years.
There is almost nothing accurate about this comment.
"Makes predictions to within a quantifiable epsilon"? What in the world do you mean? The industry experience with C++ is that it is extremely difficult (i.e., expensive) to get right, and C++20 or newer does not change anything about that. Whatever "epsilon" you are talking about here surely has to be very large for a number bearing that sobriquet.
As for the mindless anti-Rust slander... I'm not sure it's worth addressing, because it reflects a complete lack of the faintest idea about what it actually does, or what problem it solves. Let me just say there's a reason the Rust community is rife with highly competent C++ refugees.
> "Vibe coding" will be a more colorful and better remembered mile marker of this lousy decade in computers than Rust, which will be an obscurity in an appendix in 100 years.
I doubt it.
I'm teaching a course on C this fall. As textbook I've chosen "Modern C" by Jens Gustedt (updated for C23).
I'm asked by students "Why don't you choose K&R like everyone else?"
And while the book is from 1978 (ANSI C edition in 1988), and something I've read joyously more than once, I'm reminded of how decades of C programmers have been doing things "the old way" because that's how they're taught. As a result, the world is made of old C programs.
With this momentum of religiously rewriting things in Rust we see in the last few years (how many other languages have rewritten OpenSSL and the GNU coreutils?), the amount of things we depend on that was incidentally rewritten in Rust grows significantly.
Hopefully people won't be writing Rust in 100 years. Since 100 years ago mathematicians were programming mechanical calculators and analog computers, and today kids are making games. But I bet you a whole lot of infrastructure still runs Rust.
In fact, anything that is convenient to Vibe code in the coming years will drown out other languages by volume. Rust ain't so bad for vibe coding.
Kudos for going with modern C practices.
There is a place to learn about history of computing, and that is where K&R C book belongs to.
Not only is the old way, this is from the age of dumb C compilers, not taking advantage of all stuff recent standards allow compiler writers to take to next level on optimizations, not always with expected results.
Maybe getting students to understand the ISO C draft is also an interesting exercise.
I hope in 100 years we're not using any of the languages available today. I do like Rust, and use it whenever it's appropriate, but it has its warts and sharp edges. Hopefully we'll come up with something better in the next century.
> Rust makes false promises in practical situations. It invented a notion of safety that is neither well posed, nor particularly useful, nor compatible with ergonomic and efficient computing.
Please stop. Rust's promise is very simple. You get safety without the tracing GC. It also gives you tools to implement your own safe abstraction on top of unsafe, but you are mostly on your own (miri, asan, and ubsan can still be used).
Neither Rust nor Ada nor Lean nor Haskell can guarantee there are no errors in their implementations.
Similarly, none of the listed languages can even try to show that a bad actor can't write bad code or design bad hardware in a way that maintains their promises. If you need that, you need to invent the Omniscient Oracle, not a program.
I hate this oft repeated Nirvana fallacy. Yes, Rust is offering you a car with seatbelts and airbags. It is not offering a car that guarantees immortality in the event of a universe collapse.
People state these things about Rust's own implementation (or one of the other gazillion safe langs) potentially not being safe all the time, but the difference to unsafe languages is, that once any bug is fixed, everyone profits from it being fixed in the implementation of Rust. Everyone who uses the language and updates to a newer version that is, which often goes without code changes or minimal changes for a project. Now compare that with unsafe languages. Every single project needs to "fix" the same kind of safety issues over and over again. The language implementation can do almost nothing, except change the language to disallow unsafe stuff, which is not done, because people like backwards compatibility too much.
> People state these things about Rust's own implementation (or one of the other gazillion safe langs) potentially not being safe all the time
Because it's technically true. The best source of true!
Sorry, meant to say the opposite of truth. Neither Rust nor Ada.Spark, which use LLVM as a backend, can prove via their implementation there are no errors in LLVM.
In the same way, I can't guarantee tomorrow I won't be killed by a rogue planet hitting Earth at 0.3c. So I should probably start gambling and doing coke, because we might be killed tomorrow.
> Every single project needs to "fix" the same kind of safety issues over and over again
I doubt that's the biggest problem. Each of the unsafe libraries in C/C++/Zig can be perfectly safe given invariants X and Y, respectively. What happens if you have two (or more) libraries with subtly non-compatible invariants? You get non-composable libraries. You end up with the reverse problem of the NPM world.
its bad because they are mixing what was supposed to just be execution boundaries into the overall runtime engine without making it explicit how to bridge between one and another.
Sounds mostly like a documentation issue, or the lack of thereof.
Zig would be my go to language for low level stuff.
I think fact that Zig can be used as a C/C++ cross compiler is brilliant.
I’m sorry, but any non-trivial Zig code gives me PTSD flashbacks of C. I don’t understand who Zig is targeting: with pervasive mutability, manual allocation, and a lack of proper sum types, it feels like a step back from languages such as Rust. If it is indeed a different way to write code, one that embraces default memory unsafety, why would I choose it over C, which has decades of work behind it?
Am I missing some context? I’d love to hear it.
I love Zig precisely because it is so similar to C. Honestly, if you don't like C, I can totally understand why you wouldn't like Zig. But I love C, and I love Zig.
Zig has become my go-to for projects where I would previously have reached for C, largely because Zig has such good compatibility with other C projects.
Rust, on the other hand, is a completely different beast. It is very different from C, and it is far more complicated. That makes it harder to justify using, whereas Zig is a very easy choice as an alternative to using C itself.
C is entirely as complicated as Rust, if your goal is to write correct software that doesn't crash all the time. It's only a syntactically simple language. Actually making anything interesting with it is _not_ simple.
https://github.com/oxidecomputer/omicron/blob/5fd1c35/nexus/...
[delayed]
I don't think a 100-line function signature is representative, but I will point out that the alternative is at least 100 lines of runtime checks instead. In both cases, what a nightmare.
Compared to C:
Discriminated unions, error handling, comptime, defer.
Better default integer type casting, ability to choose between releaseSafe/releaseFast
And probably other things.
As for comparison to Rust, you do want very low level memory handling for writing databases as an example. It is extremely difficult to write low level libraries in Rust
I think the argument is that it is also extremely difficult to write low level libraries in Zig, just as it is in C. You will just only notice the difficulty at some later point after writing the code, potentially in production.
> low level libraries in Zig, just as it is in C
Did you write any Zig code yet? In terms of enforced correctness in the language (e.g. no integer promotion, no implicit 'dangerous' casts, null-safety, enforced error handling, etc...) and runtime safety (range-, nullptr-, integer-overflow-checks etc...), Zig is much closer to Rust than it is to C and C++.
It "just" doesn't solve static memory safety and some (admittedly important) temporal memory safety issues (aka "use-after-free"), but it still makes it much harder to accidentially trigger memory corruption as a side effect in most situations that C and C++ let slip through via a mix of compile errors and runtime checks (and you get ASAN/UBSAN automatically enabled in debug builds, a debug allocator which detects memory leaks and use-after-free for heap-allocations (unfortunately not for stack allocations), and proper runtime stack traces - things that many C/C++ toolchains are still missing or don't enable by default).
There is still one notable issue: returning a reference to stack memory from a function - this is something that many unexperienced Zig programmers seem to stumble into, especially since Zig's slice syntax looks so 'innocent' (slices look too similar to arrays, but arrays are values, while slices are references - e.g. 'fat pointers') - and which IMHO needs some sort of solution (either a compile time error via watertight escape analysis, or at least some sort runtime check which panics when trying to access 'stale' data on the stack) - and maybe giving slices their own distinct syntax that doesn't overlap with arrays might also help a bit.
I mean, there's no question that Zig, also in its current state, is vast improvement over C or even C++ - for the "small stuff". It is much more pleasant to use.
But there is still the "big stuff" - the things that have a fundamental, architectural impact. Things like: Will my program be multithreaded? Will I have many systems that interact? Will my program be maximally memory-efficient? Do I have the capacity (or money) to ensure correctness if I say "yes" to any of that?
The most important consideration in any software project is managing architectural complexity. Zig is better, yes, but not a paradigm shift. If you say "yes" to any of the above, you are in the same world of pain (or expenses) as you would be in C or C++. This is the reason that Rust is interesting: It makes things feasible/cheap that were previously very hard/expensive, at a fundamental level.
> Zig is better, yes, but not a paradigm shift.
But it doesn't have to be and it shouldn't. Rust also isn't a paradigm shift, it "just" solved static memory safety (admittedly a great engineering feat) but other languages solved memory safety too decades ago, just with more of it happening at runtime.
But in many other areas Rust copied too many bad ideas from C++ (and many of those "other things" Zig already does much better than Rust - but different people will have vastly different opinions about whether one solution is actually better than another - so discussions about those features usually run in circles).
There shouldn't be a single language that solves all problems - this will just result in stagnation, it's much better to have many languages to pick from - and even if they just differ in personal opinions of the language author of how to do things or entirely subjective 'syntax sugar' features. Competition is good, monocultures are bad.
> The most important consideration in any software project is managing architectural complexity
No language will help you managing "architectural complexity" in any meaningful way except by imposing tons of arbitrary restrictions which then get in the way for smaller projects that don't need much "architecture". We have plenty of "Enterprise-scale languages" already (Java, C#, Rust, C++, ...), what we need is more languages for small teams that don't get in the way of just getting shit done, since the really interesting and innovative stuff doesn't happen in enterprise environments but in small 'basement and bedroom teams' :)
You put "just" in scare quotes, but that word does a lot of heavy lifting there. Static memory safety is an extremely useful thing, because it enables you to do things competent programmers would never dare in C, C++, or Zig. Things like borrowing data from one thread's stack in another thread, or returning anything but `std::string` from a function. These things were simply not feasible before without a huge bulky runtime and GC.
Keep in mind that Rust's definition of "memory safety" covers much more than just use-after-free, the most important being thread safety. It is a blanket guarantee of no undefined behavior in any code that doesn't contain the word `unsafe`. Undefined behavior, including data race conditions, is a major time sink in all non-hobby C or C++ projects.
What bad ideas from C++ did Rust copy, in your opinion? I'm really not sure what you mean. Smart pointers? RAII?
There are plenty of languages that enable quick iteration, prototyping, or "just getting shit done". If that's what you need, why not use them? I'm personally more concerned about the finished product.
> You put "just" in scare quotes
...I don't use them as "scare quotes", it's more like in "can't you just..." - e.g. something that looks simple from the outside but is hard to do / complex on the inside - e.g. I do recognize the work that went into Rust's memory safety, but I question the effort that went into it compared to more traditional memory safety methods, especially when looking at the restrictions that Rust imposed on the programmer - it's a pretty hefty tradeoff (IMHO of course).
> What bad ideas from C++ did Rust copy, in your opinion?
Mainly doing things in the stdlib that should be built into the language (e.g. Option, Result, Box, Cell, RefCell, Arc, and probably a dozen other types and traits...), resulting in what I call 'bird droppings syntax' of too many chained function calls to get to the thing you actually want (.unwrap, .into_iter, .iter, .iter_mut, .as_ref, .as_mut, .expect, .unwrap_or_else, .unwrap_or_default, .ok, .ok_or_else, .and_then, .or_else ... like, wtf?). The absurd amount of `::` and `<>` in typical Rust code. The missing separation line between stdlib and language (like for-loops using the Iterator trait, or more obviously: operator overloading). The stdlib doing memory allocations behind your back through a global allocator - which we know from C++ do be a really bad idea for decades already... etc etc... I think most Rust programmers are blind towards those issues the same way that C++ programmers are blind towards C++ issues (which is another thing both language ecosystems have in common though).
I mean, there's no winning here. Either the language is too complex and does too many things, or it's not complex enough and relegates fundamental things to the standard library.
I don't thing there is any substantial difference between `Option<Thing>` and `@Nullable Thing` or `Thing | null`, I don't think there's anything wrong with choosing `::` over `.` for namespace resolution (it means you can have local variables with the same name as a module), and you have to have some way to declare generic parameters.
Rust generally does not allocate behind your back, but custom allocators is a work in progress. The reason it takes time is precisely that they want to avoid the mistakes of C++. A couple of mistakes were already avoided - for example, async/await in Rust does not allocate behind your back, while C++ coroutines do.
> I think the argument is that it is also extremely difficult to write low level libraries in Zig, just as it is in C.
This has been not my experience at all in the ~6 years I've been writing Zig. I started having very little experience writing C (<1000, lines all written while in university) and since day 1 Zig has been a tremendous improvement over it, even back when it was at version 0.4.0.
Glad you're having a great time with it. :-)
I'm informed by having shipped a lot of C++ code in my time, which has taught me a lot about actually delivering stable software. Being better than C is a very low bar here.
> a lack of proper sum types
Do you consider Rust enums 'proper sum types'? If yes what are Zig's tagged unions missing?
E.g.:
Zig is a systems programming language. I think that's probably who it's targeting.
People do systems programming in rust, but that's not really what most of the community is doing. And it's DEFINITELY not what the standard library is designed for.
>People do systems programming in rust, but that's not really what most of the community is doing.
As someone who haven't done any systems programming after university: wait, what?
I was under impression that this is exactly what people where doing with Rust.(system apps, even linux kernel, no?)
If not - what do they (most if the community) are doing with Rust?
Web servers, games, and applications, that sort of thing.
Some people definitely do systems programming in, but it's a minority. The std library is not set up for it at all, you need something like rustix, but even that results in very unidiomatic ("unsafe") rust code.
In Zig it's all in the std library by default. Because it's a systems programming language, first and foremost.
Actually I was also under OPs impression... can you tell me few specific problems with using rust for systems programming? BTW, I have only ever done something that resembles systems programming in C.
Which part of the Rust standard library are you referring to here?
As far as I can tell, it contains many, many features that are irrelevant outside of systems programming scenarios with highly particular needs.
Let me answer your question with a question - how do you memory map in rust with the standard library?
In zig it's std.posix.mmap.
Because Rust's standard library doesn't provide memory mapping you will need to use platform specific APIs.
In Zig it's exactly the same except that they decided to provide the POSIX platform specific APIs, which if you're using a POSIX system is I guess useful and otherwise it's dead weight.
It's a choice. I don't think it's a good choice but it's a choice.
I think you are moving the goal posts. You use the `memmap2` crate, or the `libc` crate if you want to be reckless about it. The question was how the standard library gets in your way, not whether it includes everything you need.
And I don't think that including every feature of every possible OS is a sensible position to have for a standard library. Or would you argue that it should also include things like `CreateWindowExW()`?
If all you use is the Rust standard library, you can be reasonably sure that your program works on all platforms, and memory mapping is something that is highly platform specific, even among POSIX-likes. I would not like to attempt designing a singular cross-platform API for it that has to be maintained in perpetuity.
(There are a few OS-specific APIs in the Rust standard library, mostly because they are required anyway for things like I/O and process management. But the limit has to be set somewhere.)
Your question might hint at a questionable presumption. So let me answer your question with a question - Does one have to memory map in Rust? Perhaps there are alternatives available in Rust, that you are not considering.
If you are too dumb for it I don't have any hope
The new I/O interface makes printing a simple “Hello, world!” more complicated, but once you get used to it, the design is actually very clean, versatile, and future-proof.
Since 0.15, though, I feel too dumb for Zig’s ArrayList.
Is it future-proof though? Last I saw, it relied on some yet-to-be-determined design for compiling async variants of everything that uses IO, and it was still unclear whether it was possible at all to support dynamic dispatch.
My info could be outdated - I don't follow Zig very closely, but I am curious.
The Reader/Writer changes are perfectly compatible with the upcoming async I/O stuff and you won't need to change any code that just deals with streams.
No promises about potential future changes though :^)