Four Months of Zig
68 points by Defelo
68 points by Defelo
This contrasts to Rust, which leads programmers to insert panics into their code.
Inserting panics is definitely not the norm.
In Rust you can use ?, which is quicker than unwrap, but you also have to do something like Result<Type, Box<dyn std::error::Error>. That's a mouthful! Alternatively, you can use the anyhow crate and return anyhow::Result, which also provides some nice helper methods. But then you have to add a dependency for reasonable error handling. There's also Zig-style composable errors with yet another library.
You definitely don't have to return a boxed error. In fact I consider that a code smell. The alternative not discussed here is to define your error enum like you would with thiserror and then implement the Error trait yourself. This is totally doable, and not that much of a pain if you're wanting to avoid a dependency.
Nitpicks aside, one of the things I struggle with in Zig sometimes is that starting a new project can be a worst case scenario for an ADHD brain. When I'm starting a new project I want to jump right in to tinkering on the problem, and instead I find myself wiring up similar functionality in build.zig that I use in many projects (check step to help out ZLS, test steps, etc). By the time I'm satisfied with my build.zig I've already spent a chunk of my dopamine budget on working through a part of the project I find aggressively uninteresting 😅
Anyway, @liberty definitely come say hi in Portland :)
Inserting panics is definitely not the norm.
It's common to panic when prototyping, since it's the easiest way to handle errors. Sometimes these remain in production code because it's easy to forget about them. This is worsened when there are many examples online that both train new developers and LLMs how to write code.
I've seen Rust panics in apps I've used regularly and also have seen expect abused to produce error messages (I've done this when prototyping, so I'm also guilty).
In Zig, on the other hand, the easiest way to handle errors is to bubble them up. This is much easier to add proper error handling to later on, and doesn't crash the whole application.
You definitely don't have to return a boxed error. In fact I consider that a code smell. The alternative not discussed here is to define your error enum like you would with thiserror and then implement the Error trait yourself. This is totally doable, and not that much of a pain if you're wanting to avoid a dependency.
I have done this before. It's much more annoying to do than in Zig and doesn't handle composition nearly as cleanly.
.. sometimes is that starting a new project can be a worst case scenario for an ADHD brain ...
That's fair. Have you considered having a basic build.zig you use for your projects? Copy/paste is the time-old solution to boilerplate after all. I'm curious if there's anything that's specific to each project that prevents that. Maybe it'd be a good feature to have in ZLS?
Anyway, @liberty definitely come say hi in Portland :)
It's a bit of a trip but I'll definitely have to sometime!
Sometimes these remain in production code because it's easy to forget about them.
If one is through with the prototyping stage and doesn't want to grep for unwrap( or expect(, the Clippy lints "unwrap_used" and "expect_used" will forbid these automatically.
I use Clippy this way liberally. Unfortunately there's a whole ecosystem to consider that may not.
Also, it's generally the case that the code that is written first tends to be the code that remains (which is why TigerStyle emphasizes getting things right the first time).
To get things right the first time is not actionable advice, as no one endeavors to get things wrong. To gradually increase the level of confidence in a code base over time we gradually increase the level of rigor to which we subject it, which includes increasing the scope of static analysis once code is no longer in the prototyping phase, easily done via automatic enforcement in CI.
It's nice to see a second blog post on a positive experience from the last Zig Day Seattle. Positive feedback keeps these events happening, not that they're hard to organize once you've found a location that works.
I may even head down to Portland to catch one before the next local one comes around.
This is certainly do-able with the next Zig Day Portland being on Novemeber 7th, but the next Zig Day Seattle is the very next day on November 8th, so it's not like you'd have to wait long. If this post piques anyone's interest in Zig and the Bellevue library isn't a hard place to get to, please join us as newbies are more than welcome! I made sure we got the big room this time just in case, hah.
Thanks for organizing these! I didn't realize there was a Zig meetup in the Seattle area. Unfortunately I won't be able to come to the one on November 7th, but I've signed up to the mailing list and I look forward to meeting everyone at Zig Day 4. Please keep running these!
I actually intended to come down to Portland a couple weeks ago, which was after I wrote those words. Some things interfered with those plans though. Definitely plan on coming to the next Zig Day Seattle!
It was previously written in Rust but I grew tired of the massive dependency trees and want to take full advantage of Zig's build system.
I'm wondering what prevents the author from stripping away dependencies in Rust.
This is answered in the article. It boils down to:
It's possible but painful to make Rust work without large dependency trees. I do hope for the Rust ecosystem to make strides in a better direction here like they are in other places.
Zig avoids both through dependency culture and having basic stuff like error handling and serialization in the language or standard library.
I do hope for the Rust ecosystem to make strides in a better direction here like they are in other places.
Amen to that! As a heavy Rust user and longtime fan, the current dependency situation (and culture) is maybe my biggest annoyance with the language/ecosystem.
It's weird how far the dialog has shifted on this from when I started using Rust 8ish years ago (of course, people talking about this today are not the people that were talking about it then). People thought std did too much!
Zig avoids both through dependency culture and having basic stuff like error handling and serialization in the language or standard library.
tbf Zig avoids it by having no stability guarantees on its standard library, yet. Keeping things out of std is a deliberate policy choice to keep stability and avoiding committing to APIs forever, while some things have eventually been absorbed or implemented within std (lazy_static/OnceCell, iterools combinators, futures, crossbeam channel internals iirc, to name a few).
I was going to ask what Zig has that Rust doesn't (genuine question, I have not spent much time with Zig yet). I'm surprised you say error handling, but serialization is a big one!
What it has within its stdlib over rust: libc bindings, PRNG, Timezone parsing, URI, base64, JSON, DEFLATE, LZMA(2)/XZ/ZSTD decode, Tar, Zip extract, Async executor, http client & server, cryptography
and even some odd ones: Dynamic library loading, Terminal progress bar, utf16/wtf16, COFF/ELF/MACHO parsing, DWARF/PDB parsing, SPIR-V target bindings, Zig language parsing & formatting (AST, IR)
In other words, "our stdlib is made of things we extracted from our compiler". Or maybe it's the other way around, "our stdlib is made of things we needed in our compiler".
Either way, it looks like nature of included batteries is heavily biased towards "it's what our compiler needs", rather than anyone being truly concerned with making a well-rounded stdlib.
I'm not sure I consider that a standard to aspire to?
I'm not sure I consider that a standard to aspire to?
Design of the standard library is on the roadmap but while we're still working on the compiler the stdlib grows organically based on it's needs. Status quo is not our end goal.
That said, this doensn't change the fact that those definitions are available to Zig users, and that it's likely that they will remain as they are actively used by the compiler.
Are you sure that the Zig compiler contains an HTTP client and server, timezone parsing, random numbers and cryptography, tar and zip file extraction? (If this is the case, please reply, I'm curious.)
Dynamic library loading is not odd. C standard libraries have it, and Zig is a C replacement. On Linux at least, you can't program a GPU without it, because GPU drivers are dynamically loaded libraries. Zig has its own implementation of dynamic library loading, which means that the Zig standard library needs to be able to parse dynamic libraries on all supported platforms, meaning COFF, ELF, MACHO.
I don't believe the compiler itself needs those, but the package manager needs most of them since it fetches and extracts packages, including over TLS. The fuzzer uses the Web server. That said, Zig's criteria for standard library inclusion appears to be generally broader than Rust's.
Thank you! I had the feeling when I asked that I was missing maybe one thing; but reading these I'm like "yup, could use that" for at least half of them. Much appreciated.