Hacker News new | past | comments | ask | show | jobs | submit login
Zig 0.12.0 Release Notes (ziglang.org)
119 points by GalaxySnail 7 months ago | hide | past | favorite | 58 comments



Small self plug: https://zig.guide is ready for Zig 0.12.

Some notable changes in zig.guide since Zig 0.11 came out:

  - Changed from following master, to following major releases (Zig is more stable now, less work for me. Yay.).

  - Migrated from Hugo to Docusaurus, bringing some large frontend improvements.

  - Moved over from ziglearn.org to zig.guide.

  - Removed 'usage in production is not recommended' from the front page.

  - Support for versioning.

  - Greatly improved testing through build.zig.

  - Opened up for github sponsorships.

  - Added another section for supplementary walkthroughs.
The site still isn't nearly where I want it to be but there's been some big improvements in the last few months.


  - Migrated from Hugo to Docusaurus, bringing some large frontend improvements.
Does this mean Docusaurus has more or better features? I do not know either of them very well but looking for a solution to publish md docs.


You could probably make a good docs site in hugo with enough work, but Docusaurus has pretty much everything I need out of the box (and is less annoying to extend). I think it really is a lot better for documentation.


I love the idea of Zig, but it really bothers me that they haven’t put 100% total priority on stdlib reliability and asyncrony.

It kinda boggles my mind in particular that they’ve left async unfinished all this time. I can argue myself into tolerating an unfinished stdlib until the 1.0 release, since at least the interface should be pretty stable, but it’s been years, and async is an essential differentiator in this domain that needed to be nailed down early so that an ecology and craft theory could start developing around it. Instead, not only does Zig still lack a production-quality stdlib today, but it’s basically a nonstarter against the main contenders without proper async.

Maybe the thing to do would have been to offer some type of sanitary minimal API for a stack object + state machine + set/longjmp builtin primitives that have well-defined semantics so that at least people could get started defining usable APIs on top of it and experimenting with it.

Instead there was a false start on it with a short stop.

I don’t understand the mindset that led to this (non-)decision, but it erodes my confidence in the project. I understand that it’s not meant to inspire confidence and that it’s still experimental, but at this timescale it is simply frustrating to see and wish for a better status.

I know, I’m not wearing their shoes or whatever. Maybe I’m being uncharitable, but I don’t think I could really be accused of being impatient at this point.

In fairness, I feel this is a major hole in nearly everything but Go, so I guess getting it right is important.


Tbh, I don't understand the fixation on async/await in any language, and the last place where I would want to have that baked in is the stdlib (instead even with async/await available the lowest 'async stdlib layer' should use simple completion callbacks, which can then be wrapped in a higher level async/await compatible API if desired - although the original Zig async/await actually had a solution for the function color problem, which made such an 'async-agnostic' layering less important).

Async/await is a nice-to-have syntax sugar, but not required to write asynchronous code, and considering the substantial under-the-hood complexity I'm not sure if async/await is a good thing overall, especially in a low level systems programming language like Zig (or FWIW, also Rust and C++).

Arguably, promises are an important concept in JS because it's a convenient and composable method to let code complete "in the background" without proper multithreading, but promises work just fine without the async/await code transformation magic (which even in JS is just the cherry on the top, but not a must-have feature).

> sanitary minimal API for a stack object + state machine + set/longjmp builtin primitives

Traditional async/await doesn't use most of that but instead relies on the compiler to transform sequential 'async code' into a switch-case state machine. What you want sounds more like green threads / fibers with stack switching. This isn't portable to some platforms where the call stack isn't accessible (like WASM). But as I wrote above, I think that lowest layer should simply be 'stdlib functions with completion callbacks'. Those low level functions can then be wrapped into all sorts of asynchronous/multithreading runtimes.

FWIW, I think a stack-switching fiber runtime should be possible in Zig without special language primitives, because Zig has inline assembly: https://ziglang.org/documentation/master/#Assembly


Do you think it’s good or bad for Rust that they got builtin async semantics this late in their lifecycle, after multiple people have already contributed competing and incompatible asynchrony APIs? Have you paid any attention to the state of industrial asynchronous programming in C++?

I can see arguing to omit it entirely from a language, and to never introduce it, but clearly the Zig authors intend to include it.


I'm not closely following the state of async/await in Rust and C++, but from my outside perspective it looks like it resulted in a hot mess in both cases.

Zig's initial async/await solution looked like it would avoid a lot of that mess, but IMHO it's better to not have a feature at all instead of a half-assed feature. So I actually appreciate Zig to focus on other things first if they don't currently have a good plan on how to provide a good async/await implementation that fits the "Zig vision". Personally, it's definitely not anywhere close to the top of my "must have" list for a low level language like Zig.


In Rust, C, and C++, people have come up with their own solutions to it, which are incompatible with one another, hard to debug, and have caused ecosystem fragmentation. The reality is that evented IO is a system feature that needs to be supported in the standard library first and foremost, if not in the compiler, to avoid competing “async std” implementations, and then a blessed API should be provided to support it so that the ecosystem doesn’t diverge. Go knocked it out of the park on this.


Yes. I think I don't understand something, because without a cohesive concurrency story, it isn't clear how you're supposed to glue different components together effectively.

For example, an http server is using nonblocking calls and dispatches to its own threadpool. The lack of cohesiveness means that the blocking call made by the PostgreSQL library can't feedback into the http server's concurrency model. The http server could expose or pass some control mechanism into the application, but for that to work, all the libraries would need to support it.

A lot of people don't seem to think this is necessary, and I've generally assumed that they're right and there's stuff I just don't understand.


It's kinda true if you're just using the system primitives directly (kqueue, epoll, whatever) but people tend to build abstractions over those and then offer callback scheduling libraries for their users, and away we go.

IMO, we haven't really seen a natively compiled and manually memory-managed language "get it right" yet. There are pretty good third-party abstractions for it though.


> async is an essential differentiator in this domain that needed to be nailed down early

Which domain are we talking about here?

> a major hole in nearly everything but Go

... because Go being mostly used as "the devops tool language" where I honestly don't see much if any point to async code.


Go (and erlang) don't really have async, in the same way. In both of these languages, every function has implicit async yield points (this is? was? actually problematic in go since a for loop with no function calls can? could in the past? lock up the CPU), but in any case it's not the same conversation as rust/c/c++/zig which have the form of async where you have to explicitly declare your yield points by nature of the effective lack of runtime, or python/js where the vm is not smart enough to put yield points in for you.


If anything I think Go is mostly used to implement client and (perhaps moreso) server apps using HTTP and other RPC protocols.


IMHO the fact that Zig already gained a lot of track in software development does bring some responsibilities whether the devs like it or not. If you create a language that good and something that is actually taken seriously for tackling the behemoth task of being the actual predecessor for C that has been around half a century, then you CANNOT simply excuse weak points with "the language is not done, yet.".

No sorry for you guys, you made some freaking good stuff, people wanna use it in production already, deal with your success. And there aren't even many pain points with the language itself. Most of them are meta. Here are my pain points:

- Documentation: Step it up... this is a mess. I might be able to learn it because I learned many languages over the years, but many people will just give up frustrated and never turn back. And even so I am mostly OK with the docs, I still miss stuff.

One of the worst things could as well be its own point: Document the build system darn it. It is so hard to learn, that I prefer using any other build system or even creating manual zig commands.

- ZLS needs to be merged as part of the project. The fact that it is not always on par with every commit is annoying. A language server should be an inseparable part of a language nowadays.

- And now to one of the few pain points I have with the language itself: You cannot be serious about that "underscore, unused variable" solution. That is so extremely ugly and code polluting.

I know this critique was harsh, but it is because I love this language and I think it will take off so heavily :). My fear is that if those things are not considered that at a certain point users will be annoyed by Zig and it will lose a big amount of userbase.


Considering that the language is "not done yet", I think the documentation is quite ok for now. But I'd really like a proper language specification, similar to https://go.dev/ref/spec, updated in lockstep with the implementation. I also agree that the build system needs some minimal documentation. It is a bit arcane without it.

Considering that a major goal of Zig is to build and maintain robust software, the decision of making unused variables an error is interesting and worth exploring. It works pretty well in practice when we use ZLS, which goes back to your point about making ZLS part of the standard toolchain.

> deal with your success

And how exactly should the Zig team deal with its success? That's a small team, with limited funding. Do you think they should prioritize the pain points you listed over the ones in their current roadmap?


This isn't specific to Zig. But most thing you said is a feature not a bug. Let me quote from the release note.

>This Release Contains Bugs, Zig has known bugs and even some miscompilations.

>Zig is immature. Even with Zig 0.12.0, working on a non-trivial project using Zig will likely require participating in the development process.

I dont think documentation should be priority for Zig at this stage, just read the Release Note and you will see how things are changing and breaking all the time. But I do agree certain things should be documented, as suggested in another reply is the Spec and build system.


Seems, Java/C#/C++ more to your taste where corporate behemoths behind them can put more people on documentation and assorted tooling than zig has for core development.

> My fear is that if those things are not considered that at a certain point users will be annoyed by Zig and it will lose a big amount of userbase.

They won't use much user base as that kind of users will remain committed to using corporate managed languages.



I started learning zig to build electrobun a couple months ago [1].

It’s very much like low level go and feels like home for people with Typescript experience.

Even with all the little quirks and gaps, it’s my new fav language.

The other day I ported a c implementation of bsdiff to zig and was able to make it go 20% faster and compress patches 10% smaller.

Very excited for the future of zig. The stated mission of replacing c and mindset of the people building it align well with how I think. Can’t wait to update to 0.12.0 when I’m back at my laptop.

[1] https://github.com/blackboardsh/electrobun


Zig is, I think the only language, that at first didn't catch my attention, but slowly grew on me. It started off by finding out zig cc.


If you like writing Zig, Bun is hiring systems engineers in San Francisco. We are one of the largest Zig projects.

https://bun.sh/careers


What's your business model? Where do you get money to pay people?


From an archived announcement page of Oven, the company behind Bun:

> Oven has raised $7m in funding led by Bucky at Kleiner Perkins with participation from Guillermo Rauch, YCombinator’s Continuity Fund and more.

> Oven will provide incredibly fast serverless hosting & continuous integration for backend & frontend JavaScript apps — and it will be powered by Bun.

> The plan is to run our own servers on the edge in datacenters around the world. Oven will leverage end-to-end integration of the entire JavaScript stack (down to the hardware) to make new things possible.

https://archive.ph/1gnVt


Hey Jarred; Nice to see you here.

Great work on Bun. Excited to see the future of your project.


I'm 100% hooked on Zig, don't really wanna use another language anymore

Reading other people's Zig code is usually a joy, as is writing it


Congrats to the team! I've been regularly using Zig for a bit over a year now and it's been fantastic. I feel as though it's helped me grow as a programmer as well as has allowed me to build some cool stuff. It's a pleasure to use!


  const connection = try http_server.accept();
  defer connection.stream.close();
This pattern of reaching into the returned struct's internals is so weird to me. Why not just connection.close()?

  Unnecessary Use of var
Sounds like yet another case of "this should be a warning instead".


A violation of the Law of Demeter.


> Sounds like yet another case of "this should be a warning instead".

The discussion about it is indeed interesting: https://github.com/ziglang/zig/issues/224

e.g.:

> > When debugging/prototyping, it's useful to comment out a line without having to refactor, e.g. > > I think this use case is a strong argument. However, it's up against another strong use case, which is the ability to rely on a set of assumptions when reading the source of a compiling codebase. > [...] > a variable declared with var does have a modification to it

Which is an interesting property.

We'll see how it goes for them.


> Sounds like yet another case of "this should be a warning instead".

Agreed. There should at least be a way to turn this into a warning during development.


If it takes the same direction as the controversial "unused vars are errors" feature, IDE integrations will take care of that. For instance in VSCode the unused-var-error has become a complete non-issue taken care of by autoformatting through the Zig language server by inserting or removing '_ = unused_var;' as needed.

But TBH, I'm using similar strict error settings in TS/ESLint for a long time now (as part of our company linter rules) without autoformatting and it's fine.


> For instance in VSCode the unused-var-error has become a complete non-issue taken care of by autoformatting through the Zig language server by inserting or removing '_ = unused_var;' as needed.

this doesn't help anyone writing autogen code, though.


> IDE integrations will take care of that

So this is an IDE-mandatory language which does not come with an IDE?

> autoformatting through the Zig language server by inserting or removing '_ = unused_var;' as needed.

So the error has become a non issue by completely undermining it? That seems like the opposite of a good idea.

> I'm using similar strict error settings in TS/ESLint

A linter or an IDE "error" is equivalent to a compiler warning: they don't prevent compiling and running the code.

And JS/TS's const is much weaker than Zig's, essentially trivial: zig's const means the value is immutable, not just the binding.


> So this is an IDE-mandatory language which does not come with an IDE?

Nope. AFAIK the autofixing is actually implemented by `zig fmt`. The IDE integration just makes that feature available as "VSCode Formatter".

> So the error has become a non issue by completely undermining it? That seems like the opposite of a good idea.

Nope. The important part is that the autofix works in both directions. E.g. if an unused-fix was added before, it will be removed as soon as the variable is used. This makes the development workflow painless, but at the same time the code cannot become littered with redundant unused-fixes. It's also trivial to search for the "_ =" discard pattern to find any leftover discards (in a 'final' codebase there shouldn't be any of those).

> A linter or an IDE "error" is equivalent to a compiler warning: they don't prevent compiling and running the code.

Nope, in our case it's an actual hard compile error. You can't "npm build" or even git-commit a project that has unused variables (which are not explicitly discarded).


> Nope.

Except yes, and your clarifications confirm it. If the formatter automatically adds and removes artificial uses for unused variables then you're undermining the diagnostic since you're automatically suppressing it. It's completely useless. Worse than useless even.

Not to mention zig's diagnostic is as crap as go's: it doesn't track unused stores, so it only catches a minuscule number of possible issues, and misses the much larger one, yielding both incredible annoyance and false confidence, all because the maintainers of both languages refuse to support warnings.

> It's also trivial to search for the "_ =" discard pattern to find any leftover discards (in a 'final' codebase there shouldn't be any of those).

Meanwhile a warning actually tells you directly and reminds you, without having to "search for the discard pattern".

> Nope, in our case it's an actual hard compile error.

Boy am I happy not to work with you.


> Boy am I happy not to work with you.

It wasn't my decision to introduce such draconic rules, but in practice it turned out to be a non-issue. Production code should be warning-free, and warnings should be treated as errors. Two simple rules that both make a lot of sense, no matter what language.

PS: that 'unused variables are errors' linter rule is actually coming from the airbnb eslint plugin (which AFAIK is quite popular in the JS/TS world: https://github.com/airbnb/javascript/blob/c25bce83be4db06e6a...)


> It's completely useless

It seems like you're forgetting that it shows up in diffs (code reviews) and text editors.


Thanks for reminding me, it does bloat up the code and require nitpick review comments if you catch it, and useless churn afterwards if you don’t and need to fix the code later on.

So it’s worse than useless, it’s actively harmful.


Note TLS/HTTPS is still not working

https://github.com/ziglang/zig/issues/14172


it's working perfectly fine, it just has not met the project's high standard of test coverage yet.


I think there are at least 2 serious issues with TLS, both documented in: https://github.com/ziglang/zig/issues/15226


are you nuts? TLS 1.2 is not supported at all


Congrats, I find myself more and more interested.


I've written a lot of Zig, including a somewhat popular http framework, a driver for PostgreSQL and one for DuckDB.

Personally, I feel that if you want to grow as a programmer, no matter what you're doing, there's little that offers more bang for your buck than learning C. You can pick up C relatively quickly, and that knowledge will serve you (maybe mostly indirectly) throughout your entire career.

Learning Zig is a much better option. Too many quality of life improvements over C, and you still learn the same fundamentals. If you're interested, I wrote something that might help (1).

As for _using_ Zig. I think it's a good idea to take a conservative view and assume the ecosystem is hostile. The quality of test coverage of the standard library varies. Breaking changes are frequent and aren't always trivial. 3rd party libraries are often abandoned and many are written by people learning Zig (including my own!). It's really great for learning/side/fun project because you can get distracted for weeks writing something you never intended to.

It's not actively hostile, but I think approaching it that way will help manage expectations.

I would strongly consider tracking the master branch. If you write anything substantial in Zig, you will 100% run into a must-have bug-fix or feature in the stdlib or 3rd party library that depends on on 0.13.x development commit.

(1) - https://www.openmymind.net/learning_zig/


Zig is positioned as a sort of "better C", correct?

With that in mind, how does Zig compare to C, complexity-wise? Both for learning, and its implementation.

Is it a 'bigger' language than C? Just different? Easier to grasp? Or takes more effort to get a good understanding of? About the same as C?

Anyway: a very promising project. Nice to see it progressing towards a mature ecosystem.


IMHO (not the parent): from the perspective of a C programmer, Zig is several things:

1. A "better C" in the sense that the language core of Zig fixes the commonly known and accepted design warts of C which are hard to fix because of backward compatibility requirements - this part is actually less complex and easier to learn than C because there's less implicit and less unexpected hidden behaviour while having roughly the same "language surface". If you are used to C's somewhat sloppy "hippie-approach" to programming, be prepared that the Zig compiler will yell at you a lot at the start of the learning phase, especially when it comes to implicit type conversions which follow very strict rules in Zig (when compared to C).

2. A much richer and actually useful stdlib (in this area Zig is also a much better C++), don't quite expect a Python-style "batteries included" stdlib though.

3. A much more complete compiler toolchain package, with a build system, package manager, C/C++/ObjC integration and cross-compilation to the important target platforms included and working out of the box (and most of that integrated into the Zig executable, a vanilla Zig install actually only has a single executable, the Zig compiler).

4. And finally the "visionary part", where Zig differs most from C and other languages and explores its own path, like the approach to comptime, generics, type reflection and error handling.


Even if you never use zig generics --if nothing else eliminating fucking spiral types is enough of a quality of life improvement alone that you should consider zig over C.

if you've never heard of them:

https://c-faq.com/decl/spiral.anderson.html

I'm not even sure if the faq is in jest or not.


The clockwise/spiral rule is a misunderstanding, it doesn't exist and C type syntax is not that complicated.

The thing that isn't well known about C type syntax is that there is no type syntax. At least originally, there wasn't really type syntax. (This was later muddied by e.g. types in function signature parameters, which came from C++ -- and which are a good thing to be clear).

Types follow usage (given as normal code expressions), and if you can read normal expressions, you'll know what a type declaration means.

The syntax is simply: type-name EXPR. where type-name is a simple name (or tagged name, such as struct mystruct), and EXPR is a limited subset of normal expressions. Example: int *foo[10]; Read like (type-name = int), (EXPR = *foo[10]). Now, if you now how to understand the EXPR, you'll know what the type of the variable foo is. Since (*foo[10]) must be int, you can simply go backward to know the "shape" of foo.

Similarly, there is some elegance how to define a type name instead of a variable -- you simply add the "typedef" keyword and that's it.

Even though that idea is a bit arcance admittedly, and not so pure anymore today, now I'm used to the C way of declaring types, and I think I like the fact that there isn't much of an additional syntax. Maybe it even removes mental overhead when reading code.


That's actually a problem. In c, variable declarations are not context-free.

    mytype *foo;
Could be a variable declaration, Or is it?

Suppose you had

    int mytype = 42;
    int foo = 1;
Before it. The C compiler will happily turn the statement into a multiplication with the result going to /dev/null

    var foo: mytype;
Can never be parsed with any other meaning depending on how the chips fall after parsing (and possibly analysis, given that macros are a thing) the entirety of the file.


Yes it comes with its problems. The context sensitivity is annoying and it makes creating good tooling hard. But given macros it doesn't worsen the situation much anyway.

For writing a batch parser/compiler that properly reads all the includes too, the context sensitivity is only a theoretical problem. Adding a type table is easy.

Not having to type "var" all the time is very nice on the other hand.


Clarity is always better than saving keystrokes. Code is generally more read than written.


Adding keystrokes is not the same as adding clarity, though.


This "spiral type notation" has been acknowledged in K&R 2nd Edition as one of the less fortunate design decisions in C.


Ironically, almost 40 years later Pascal linage of languages finally have their revenge in type declarations.


To add more concrete examples, you could look at tagged unions, errorsets and optionals.

An `if` statement in Zig has a few different faces, traditional condition, unwrapping an optional and unwrapping an errorset.

That makes the language "bigger", but these are things you'll regularly face in C anyways, even with trivial examples. I think most of the things Zig (purely as a language) has "added" to be labeled a "Better C" are all pragmatic and don't increase any cognitive load.

The other thing worth mentioning is that Zig has a number of safety advantages. I just touched up my DuckDB driver, and their C API exposes the underlying storage for a column as a (void ), the format of which depends on the column type. In Zig, I can map this to a typed tagged union. For an bigint column, instead of a (void ), I end up with an []i64.


Zig elminates the pointer/array ambiguity issue that exists in C. It does increase the complexity of the language, but the safety you get out of it is very much worth it.


Having learn systems in BASIC variants, Pascal dialects and Z80/68000/8086 before dealing with C, I always prefered that added language complexity that has saved me so many times when not using C.

Pity that even Dennis Ritchie failed to convince WG14 about fat pointers.


I still need a couple of things to win over me, a proper way to deal with use-after-free that isn't already what we have since other safe systems languages like Modula-2, and what probaly never happen due to FOSS beliefs among the community, Zig binary libs (not C bindings with double effort with Zig on both sides).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: