This feels like it should've been a git issue rather than a blog post.
No feedback from the author, no explaining what the code does at a level for people new to zig, no explanations of the fundamental problem (i.e. files, buffers, and flow of data, and how to write that as code) and how it relates to what zig is doing, no alternate solution for how zig or other languages might try to solve the problem.
I have a strong suspicion that people are upvoting this based on the title of 'Zig' and 'unsafe' instead of the actual substance of the post.
I've never worked with Zig in my life, and I don't have an opinion on it either. I've heard it's neat. Despite that, this post was intuitively clear to me.
Zig's *std.Io.Reader and Writer seem to be abstractions over many types, like Java's interface class, Go's interfaces, C++'s concepts or Rust's traits.
However, it seems that abstraction itself is leaky - in that the length of the buffer is an implicit dependency which cannot be known from the type alone.
Admittedly, we don't know if there's a good explanation for the behavior - but then again, HN comments are as good a place to learn as any. Maybe someone will come up and explain.
Decompress's Reader shouldn't depend on the size of the buffer of the writer passed in to its "stream" implementation.
So that's a bug in the Decompress Reader implementation.
The article confuses a bug in a specific Reader implementation with a problem with the Writer interface generally.
(If a reader really wants to impose some chunking limitation for some reason, then it should return an error in the invalid case, not go into an infinite loop.)
What a terrible way to take constructive feedback.
Writing not one but two blog posts takes time and effort, moreso when it includes investigation and examples... the posts are not just low effort "this is crap" rants (and they even blame themselves, even though it's clear from the posts that there are documentation/discoverability issues).
What if the poster doesn't feel knowledgeable enough to contribute code? (as shown by his first post title, "I'm too dumb for...")
If that's not collaborating I don't know what it is...
That's not the read I got from Andrew's comment, or the situation. If the poster doesn't feel knowledgeable/able enough to collaborate in the community discussions (like the issue links, which don't require contributing code) then doing individual blog posts instead is only going to give even worse results for everyone.
Collaborating would be contacting the Zig team through one of the many channels available and asking questions, offering suggestions etc. Posting critical blog posts without doing this first is counter-productive, and can even be seen as self promotion. After all, we're now discussing the blog posts, and not the actual issues. Would this have happened if the author had just sent an email to the mailing list or asked on Github?
> Kinda wish the author would attempt to collaborate rather than write stuff like this and I’m too dumb for Zig’s new IO interface but, whatever, it’s their blog so they can do what they want.
Damn, Andrew Kelley really come across as a dickhead when taking any bit of criticism about his language, often painting them as bad actors trying to sabotage the language.
This isn't the first time he repeats this behavior.
I'm not even sure that it's criticism. I read it as a genuine open question.
At least in my experience, asking questions like this through GitHub issues or a mailing list are met with negativity. I don't want to post through a channel where I'll get a snide, terse response from a maintainer. I'd much rather post to my blog audience, who I find to be generally knowledgeable and friendly enough to already be following me. And if I found an answer, I'd post about it and explain what I learned in the process, linking from the existing post.
In a lot of ways, I think Andrew's response is exactly the sort of flavor of reply that I would have expected no matter what channel the question had been posed through, and that's exactly why I wouldn't have gone through those channels if I was the author. His reply didn't answer the question aside from implying it's maybe an issue, nor did it invite feedback.
Andrew may be expressing frustration or dismay or annoyance in that statement, but he is not definitively "painting them as bad actors trying to sabotage the language". You are HEAVILY reading into his statement.
He only said he wishes the author would have taken a different approach. So what? Why does everyone have to jump in and start psychologizing or essentializing Andrew based on one paragraph?
Why does one paragraph have to say so much about who he is as a person? Even if it did piss him off for a few hours, so what? He's not allowed to wish someone took a different approach?
I tend to think Andrew Kelley is a great guy, not just technically but as a person. And I think that because I've listened to him talk for dozens of hours. I can guarantee you that that one sentence he wrote is not the beginning of a character assassination campaign against the author of this blog.
He made Zig because he wanted to put something good into the world and improve the state of software. How about we include that in our analysis of Andrew's character? I'll leave it to the reader to consider whether the multi-year full time dedication to Zig should be weighed more heavily than a personal feeling he had for two minutes that he expressed respectfully without attacking anyone's character.
In any case I've seen how communities treat the authors of important projects that touch their livelihood or habits to be unsurprised an author may lose it at times.
I know what you mean, but name-calling has got to be one of the worst ways to call for some decorum. It just leads to flame wars. (Be the change you want to see, and all that.)
Like a modern C with lessons learned. Instead of macros it uses Zig itself to execute code at runtime (comptime). Custom allocators are the norm. No hidden control flow, everything is very explicit and easy to follow.
But it’s not only the language itself, it is also the tooling around it. Single unit of compilation has some nice properties, allowing to support colorless async. Fast compile times. Being able to use existing C code easily and having optimization across language boundaries. Cross compilation out of the box. Generally caring for performance in all aspects.
So for me it is a better C, low-level but still approachable and not having so much cruft.
> Instead of macros it uses Zig itself to execute code at runtime (comptime).
Nice. FWIW, I have a vague PL design in my head that does this despite being a much higher-level language. (For that matter, I think of my idea much as "like a modern Python with lessons learned".) Point being I definitely think this is a good idea.
To my understanding, the things actually called "macros" in Lisp also do this.
> Custom allocators are the norm.
On the other hand, this doesn't sound to me like an upside. Of course it's fine and well if it's easy to do this. But hopefully you'd prefer not to have to... ?
> No hidden control flow, everything is very explicit and easy to follow.
What sort of hidden control flow do you see in C? (Are modern code bases using setjmp/longjmp in new code?) I would have thought that C++ is where that really started, via exceptions. But I also don't think most programmers see that as problematic for understanding the code.
> Single unit of compilation has some nice properties, allowing to support colorless async.
Would appreciate some explanation of the theory here. Though it does occur to me that the languages I can easily think of with "coloured" async also don't exactly statically link everything all the time.
Also, how does all of this compare to Rust, in your view?
I've seen the custom allocators mentioned many times as central to the value proposition. Do the allocators shipped with the standard library compose the system allocator, or are they completely distinct from the *alloc family shipped as part of libc?
The value proposition of Zig is it's the most ergonomic way to do systems programming I've ever seen. First class allocators, arbitrary width integer types, top notch bindings to OS syscalls in the std lib without relying on libc, and you can translate C bindings to Zig at compile time, so no FFI layers for existing libraries.
They sometimes write about how they're a general purpose language, but I strongly disagree. It's unapologetically a systems programming language; that's it's entire focus.
Zig’s undefined behavior story is not nearly as much of a minefield for one. That should be enough really. If you think the CPP is good enough for metaprogramming, I don’t know what to tell you either.
It's a language that attempts to solve more of the biggest issues with low level programming than any other current low-level programming language. Of course, there is nowhere near a universal consensus on what the biggest issues are, but here's what they are to me in order of importance (and I'm not alone):
1. Language complexity/lack of expressivity. You want code, as much as possible, to clearly express the algorithm at the level of detail needed at the language's domain, no more and no less. The level of detail in low-level languages is different from high-level languages because, for example, you want to see exactly where and when memory is allocated/freed. Both C and C++ fail at this for opposite reasons. C is often not expressive enough to express an algorithm clearly (at least not without the use of macros), and C++ is often too implicit, hiding important details that are then easy to miss [1]. These problems may affect program correctness.
2. Lack of spatial memory safety, which is the cause of the #2 and #6 most dangerous software weaknesses (https://cwe.mitre.org/top25/archive/2024/2024_cwe_top25.html). Unlike spatial memory safety, Zig doesn't guarantee the lack of temporal memory safety. This would have been very nice to have, but it isn't as important and not worth compromising on the other top points.
3. Slow build times, which may also affect correctness by slowing down the test cycle.
I don't find Zig similar to C or C++ at all (certainly not as similar as Rust is to C++). If anything, Zig's risk is in being a more revolutionary step than an evolutionary one.
---
[1]: In the late eighties/early nineties (when I first learned C++), when we thought it might be a good idea for a language to be both low-level and high-level, C++'s notion of "zero-cost abstractions" seemed very interesting and even promising (I don't recall them being given that name then, but the problem - or advantage - was right there at the beginning; e.g. whether a call uses static or dynamic dispatch is an algorithmic detail that may be of interest in low-level programming, as well as whether a destructor is called, possibly through dynamic dispatch). Now that notion feels an outdated vestige of a bygone era. I'm aware there are still C++ programmers who still believe in writing high level applications in low-level languages and still believe in zero cost abstractions, but I think the industry has clearly been going the other way, and there's no indication it may be changing direction or may have any reason to do so.
I believe that in modern times, this term refers to stuff like: transparent conversion to a low-level representation (e.g. a bit-packed integer) from a high-level, statically checked datatype - e.g. a tagged union with exhaustive pattern matching, aka ADT. This is also something C++ etc lack.
C2 is still in development [0] and in my opinion sticks most closely to C in spirit. It seems like the best way to modernize a C project incrementally because it sticks most closely.
I haven't tried any of these languages though, I genuinely enjoy writing dumb-as-bricks C despite all the downsides. Call it Stockholm Syndrome or whatever.
It's a powerful alternative to Rust and C++ for writing ultra low latency code when safety isn't important, like games or HFT. Its standard library and ecosystem have excellent support for passing around allocators and no hidden dynamic allocation (every function that allocates takes an allocator as a parameter), which makes it much harder to hit the latency pitfalls common to Rust and C++. It also encourages the latency-optimal approach of using local bump-a-pointer arena allocators where possible rather than the slow global allocator. And finally, the superior metaprogramming support makes it more convenient to use high performance techniques like struct-of-arrays.
Rusty rust and C++-y C++ are both often slower than hand-rolled C for a given function, and zig is intended to be a better way to hand-roll some C.
C as a language is obsessed with the fact that C runs everywhere. Even on a DSP with a 24-bit char and a 24-bit int. If you take out those complexities and you add 40 years of lessons on language design, you can make something a lot simpler.
Your comment was corrected or written by ChatGPT, right? It's super interesting that I can recognize these things now. The specific usage of punctuation, certain words and sentence construction.
Zig's pointless choice to avoid global constructors - something all platforms have supported for several decades by now - makes I/O extremely unwieldy. That alone prevents it from being a viable mainstream language.
There is a lot of useful technical work and ideas in it though.
Being the anti Rust alternative for people who has spent years/decades accumulating weird knowledge about the inner workings of C and/or C++ and maintain that being able to shoot your feet off at a distance when doing the wrong incantation is a critical feature to have.
I’m ranting but based the Rust/Zig discussions here on HN and the number of segfault issues in the Bun repo there is a core of truth.
Or Rust being the anti Zig alternative for people who have spent years/decades accumulating weird knowledge about the finer details of type systems and maintain that not being able to express an algorithm, or review one, without consulting a language lawyer (or becoming one) is a critical feature to have and that build times don't matter.
The point is that all languages, including those two, make very clear and sometimes significant tradeoffs that may be more or less appropriate in different circumstances or for different people, and, at least so far, no consensus about the "right" tradeoff has emerged.
There is plenty of room for both languages to exist. Engineering is all about trade offs.
Rust has some big pluses but it's not suitable for every application.
That's gross misrepresentation. He wrote _lots_ of helpful educational articles on zig that painted it in a good light. I would know because I learned a great deal from his posts when I started with zig. Recently he posted a grand total of 2 articles expressing his confusion/skepticism of new zig features, that's it.
No feedback from the author, no explaining what the code does at a level for people new to zig, no explanations of the fundamental problem (i.e. files, buffers, and flow of data, and how to write that as code) and how it relates to what zig is doing, no alternate solution for how zig or other languages might try to solve the problem.
I have a strong suspicion that people are upvoting this based on the title of 'Zig' and 'unsafe' instead of the actual substance of the post.
Zig's *std.Io.Reader and Writer seem to be abstractions over many types, like Java's interface class, Go's interfaces, C++'s concepts or Rust's traits. However, it seems that abstraction itself is leaky - in that the length of the buffer is an implicit dependency which cannot be known from the type alone.
Admittedly, we don't know if there's a good explanation for the behavior - but then again, HN comments are as good a place to learn as any. Maybe someone will come up and explain.
Decompress's Reader shouldn't depend on the size of the buffer of the writer passed in to its "stream" implementation.
So that's a bug in the Decompress Reader implementation.
The article confuses a bug in a specific Reader implementation with a problem with the Writer interface generally.
(If a reader really wants to impose some chunking limitation for some reason, then it should return an error in the invalid case, not go into an infinite loop.)
https://lobste.rs/s/js25k9/is_zig_s_new_writer_unsafe#c_ftgc...
Writing not one but two blog posts takes time and effort, moreso when it includes investigation and examples... the posts are not just low effort "this is crap" rants (and they even blame themselves, even though it's clear from the posts that there are documentation/discoverability issues).
What if the poster doesn't feel knowledgeable enough to contribute code? (as shown by his first post title, "I'm too dumb for...")
If that's not collaborating I don't know what it is...
The response IMO is constructive and invites the blog post author for further discussion.
Deleted Comment
Damn, Andrew Kelley really come across as a dickhead when taking any bit of criticism about his language, often painting them as bad actors trying to sabotage the language.
This isn't the first time he repeats this behavior.
EDIT: https://news.ycombinator.com/context?id=45119964https://news.ycombinator.com/context?id=43579569
I'm not even sure that it's criticism. I read it as a genuine open question.
At least in my experience, asking questions like this through GitHub issues or a mailing list are met with negativity. I don't want to post through a channel where I'll get a snide, terse response from a maintainer. I'd much rather post to my blog audience, who I find to be generally knowledgeable and friendly enough to already be following me. And if I found an answer, I'd post about it and explain what I learned in the process, linking from the existing post.
In a lot of ways, I think Andrew's response is exactly the sort of flavor of reply that I would have expected no matter what channel the question had been posed through, and that's exactly why I wouldn't have gone through those channels if I was the author. His reply didn't answer the question aside from implying it's maybe an issue, nor did it invite feedback.
He only said he wishes the author would have taken a different approach. So what? Why does everyone have to jump in and start psychologizing or essentializing Andrew based on one paragraph?
Why does one paragraph have to say so much about who he is as a person? Even if it did piss him off for a few hours, so what? He's not allowed to wish someone took a different approach?
I tend to think Andrew Kelley is a great guy, not just technically but as a person. And I think that because I've listened to him talk for dozens of hours. I can guarantee you that that one sentence he wrote is not the beginning of a character assassination campaign against the author of this blog.
He made Zig because he wanted to put something good into the world and improve the state of software. How about we include that in our analysis of Andrew's character? I'll leave it to the reader to consider whether the multi-year full time dedication to Zig should be weighed more heavily than a personal feeling he had for two minutes that he expressed respectfully without attacking anyone's character.
In any case I've seen how communities treat the authors of important projects that touch their livelihood or habits to be unsurprised an author may lose it at times.
Deleted Comment
What's the value proposition of Zig? It's not immediately obvious to me.
Is it kind of like the Kotlin of C, going for a better syntax/modern features but otherwise being very similar?
But it’s not only the language itself, it is also the tooling around it. Single unit of compilation has some nice properties, allowing to support colorless async. Fast compile times. Being able to use existing C code easily and having optimization across language boundaries. Cross compilation out of the box. Generally caring for performance in all aspects.
So for me it is a better C, low-level but still approachable and not having so much cruft.
Nice. FWIW, I have a vague PL design in my head that does this despite being a much higher-level language. (For that matter, I think of my idea much as "like a modern Python with lessons learned".) Point being I definitely think this is a good idea.
To my understanding, the things actually called "macros" in Lisp also do this.
> Custom allocators are the norm.
On the other hand, this doesn't sound to me like an upside. Of course it's fine and well if it's easy to do this. But hopefully you'd prefer not to have to... ?
> No hidden control flow, everything is very explicit and easy to follow.
What sort of hidden control flow do you see in C? (Are modern code bases using setjmp/longjmp in new code?) I would have thought that C++ is where that really started, via exceptions. But I also don't think most programmers see that as problematic for understanding the code.
> Single unit of compilation has some nice properties, allowing to support colorless async.
Would appreciate some explanation of the theory here. Though it does occur to me that the languages I can easily think of with "coloured" async also don't exactly statically link everything all the time.
Also, how does all of this compare to Rust, in your view?
Yet no string types. So lessons not so well learned. Zig does remove many warts in C.
A very small subset of possible lessons that could have been learned...
Great systems language though.
They sometimes write about how they're a general purpose language, but I strongly disagree. It's unapologetically a systems programming language; that's it's entire focus.
1. Language complexity/lack of expressivity. You want code, as much as possible, to clearly express the algorithm at the level of detail needed at the language's domain, no more and no less. The level of detail in low-level languages is different from high-level languages because, for example, you want to see exactly where and when memory is allocated/freed. Both C and C++ fail at this for opposite reasons. C is often not expressive enough to express an algorithm clearly (at least not without the use of macros), and C++ is often too implicit, hiding important details that are then easy to miss [1]. These problems may affect program correctness.
2. Lack of spatial memory safety, which is the cause of the #2 and #6 most dangerous software weaknesses (https://cwe.mitre.org/top25/archive/2024/2024_cwe_top25.html). Unlike spatial memory safety, Zig doesn't guarantee the lack of temporal memory safety. This would have been very nice to have, but it isn't as important and not worth compromising on the other top points.
3. Slow build times, which may also affect correctness by slowing down the test cycle.
I don't find Zig similar to C or C++ at all (certainly not as similar as Rust is to C++). If anything, Zig's risk is in being a more revolutionary step than an evolutionary one.
---
[1]: In the late eighties/early nineties (when I first learned C++), when we thought it might be a good idea for a language to be both low-level and high-level, C++'s notion of "zero-cost abstractions" seemed very interesting and even promising (I don't recall them being given that name then, but the problem - or advantage - was right there at the beginning; e.g. whether a call uses static or dynamic dispatch is an algorithmic detail that may be of interest in low-level programming, as well as whether a destructor is called, possibly through dynamic dispatch). Now that notion feels an outdated vestige of a bygone era. I'm aware there are still C++ programmers who still believe in writing high level applications in low-level languages and still believe in zero cost abstractions, but I think the industry has clearly been going the other way, and there's no indication it may be changing direction or may have any reason to do so.
I believe that in modern times, this term refers to stuff like: transparent conversion to a low-level representation (e.g. a bit-packed integer) from a high-level, statically checked datatype - e.g. a tagged union with exhaustive pattern matching, aka ADT. This is also something C++ etc lack.
I am not a C++ guy, but when Carbon is finally stable and hits the mainstream, I will definitely start trying out Carbon.
https://github.com/carbon-language/carbon-lang
There's not much languages in that space, essentially C3 and Zig, but the former is much less advanced IMHO (also in terms of tooling).
I haven't tried any of these languages though, I genuinely enjoy writing dumb-as-bricks C despite all the downsides. Call it Stockholm Syndrome or whatever.
[0] http://c2lang.org/site/introduction/evolution/
It is the route of what would have happened if Modula-2 or Object Pascal had won the industry mindshare instead of C.
I think it comes too late, we know better nowadays, so it doesn't really offer anything new.
C as a language is obsessed with the fact that C runs everywhere. Even on a DSP with a 24-bit char and a 24-bit int. If you take out those complexities and you add 40 years of lessons on language design, you can make something a lot simpler.
Also, modern online gaming with microtransactions isn't something I'd entrust to "hold my beer" languages and gaming industry development practices.
For me that sounds like a rather good value proposition. Too bad Zig never got the stackless corountine part they promised in the start.
There is a lot of useful technical work and ideas in it though.
Deleted Comment
I’m ranting but based the Rust/Zig discussions here on HN and the number of segfault issues in the Bun repo there is a core of truth.
https://github.com/oven-sh/bun/issues?q=Segfault
The point is that all languages, including those two, make very clear and sometimes significant tradeoffs that may be more or less appropriate in different circumstances or for different people, and, at least so far, no consensus about the "right" tradeoff has emerged.
https://github.com/nodejs/node/issues?q=Segfault