Zig 0.5.0 Release Notes
ziglang.orgZig looks like a really interesting language. I've been keeping my eye on it, waiting excitedly for the 1.x milestone.
Out of curiosity, what's the state of the world regarding Zig's stdlib? While the base language docs seem like they're in a pretty good state, the stdlib is still largely undocumented. Is this because it's unstable? Or is the lack of docs there due to lack of time/resources? Would love to get some clarification. Thanks!
The stdlib documentation is definitely the biggest sore spot of zig for me. It took me a little while to feel comfortable enough digging through the code to start finding what I could do with the stdlib. It slows me down programming using the language significantly.
Yeah, I always check that. The part that says "There is no stdlib documentation yet, but it is planned for the next release" links to a 2015 github issue. That's why I was wondering if it's still a priority or if there were still high level concerns about stability/curation/etc preventing docs from being written.
It's an unwillingness to implement documentation generation twice. Why do it in the c++ compiler when the goal is to become self-hosted?
I think there's a good answer to this which is, "because people really, really need documentation." So I am kicking around ideas of how this can be done without too much duplicate effort.
I see. Do you think there would be value in having someone just manually writing some interim docs (perhaps as a wiki) until the time when it can be properly generated?
This may seem like a weird place to draw experience from, but in my early days of programming, I've found the PHP docs extremely helpful largely thanks to community comments sections giving examples of how to do various things. Just some food for thought.
I think that would be problematic because the standard library is still very unstable. You can see from the release notes just how much changed from 0.4.0. A wiki page would be outdated right away.
One of the tenets of my development process with Zig is "avoid local maximums". A wiki for std lib documentation is not the long term solution to the problem, and so I won't be investing any time into it. All my time is invested in the long term solution.
Couldn’t you say this about every single feature implemented in the C++ compiler?
One thing that immediately stood out to me was the section titled “UEFI support” [1]. And there are demo projects and examples too. Neat!
[1]: https://ziglang.org/download/0.5.0/release-notes.html#toc-UE...
Zig + UEFI = Comic Neue in the BIOS.
[0] https://twitter.com/andy_kelley/status/1176561072398098432
Perhaps I could see some toy ransomware bootkit demanding the user to pay up or face using your computer with comic neue or comic sans as the default unchangeable font.
> Comic Neue
For the uninitiated:
As far as Comic Sans-like fonts go it's quite nice
It's currently my main UI font: https://imgur.com/cTLq6Oo.png
Pairs nicely with Fantasque Sans Mono (though if there was a more Comic Sans/Neue-like typeface, I'd set that as my terminal/editor font in a heartbeat).
I might be alone with this thought: Zig seemed really appealing in the beginning, but now I dislike it more and more with each release. It does not seem to keep the simplicity of C at all, it has lots of peculiarities, and it is becoming more and more bloated. I would consider it a C++ replacement at this point, not a C replacement due to its increasing complexity. One of Zig's philosophy was simplicity[1], but I do not think it applies now.
[1] https://github.com/ziglang/zig/wiki/Why-Zig-When-There-is-Al...
I do get the feeling, but I think part of it is simply that those of us that are used to C tend to underestimate its complexity. Especially when you consider various extensions which are widely used, the preprocessor magic often needed to perform essential tasks, and all the undefined behaviour.
There is really only one thing that I would say goes beyond keeping the language simple as possible: the async stuff. But I think it will probably be totally worth it. The async implementation looks a lot more elegant than anything I've seen so far, and it could be extremely useful in the embedded world, which is one of the places where C is dominant. It might make the language a bit more complicated, but it will make the resulting code much more simpler for a lot of applications, without compromising other goals like safety and efficiency.
Zig is still much, much closer to C than C++. If you don't think so, then you're vastly underestimating the complexity of C++.
Can you give examples of the peculiarities? I write cpp all day and zig is a nice change for me because of how simple it is to me
> Zig both signed and unsigned integers have undefined behavior on overflow, contrasted to only signed integers in C
make a feature unwanted in C, worse. nice.
debug and safe modes in zig disallow overflows entirely unless you specifically use the wrapping operator to clearly specify your intent. These checks are removed in speed-optimized builds.
In other words, the compiler will enforce that `a + b` does not overflow through all your testing and wherever you explicitly say such checks are required, but turns them into nops for you when you desire speed. You can let the compiler know that you explicitly want wrapping overflow (and not some undefined kind) using the wrapping operator `a +% b`.
Please check your facts: https://ziglang.org/documentation/master/#Integer-Overflow
It's actually become simpler and slimmer, with fewer peculiarities!
Could you follow up with some examples? Seems to contradict the parent but neither comment provides examples.
They got rid of the % operator for error propagation for one.
That's funny; one selling point of rust is the easy propagation of errors thanks to the '?' but zig removed the % which was the equivalent I think
The operator wasn't removed, it was replaced by a keyword. See the documentation on error handling: https://ziglang.org/documentation/master/#catch
You can still propagate errors in zig using ? like in rust. they just simplified it
> Speaking of performance, Zig is faster than C.
These kinds of claims make me so dubious for a language. Also who is trying to switch from C to get an ever-so-slightly performance improvement? That use-case goes to specialized hardware such as FPGAs.
You picked that quote from the main page, not from the release notes, here is what directly follows:
* The reference implementation uses LLVM as a backend for state of the art optimizations.
* What other projects call "Link Time Optimization" Zig does automatically.
* For native targets, advanced CPU features are enabled (-march=native), thanks to the fact that Cross-compiling is a first-class use case.
* Carefully chosen undefined behavior. For example, in Zig both signed and unsigned integers have undefined behavior on overflow, contrasted to only signed integers in C. This facilitates optimizations that are not available in C. Zig directly exposes a SIMD vector type, making it easy to write portable vectorized code.
So the argument is "exact same compiler as C/C++, but more opportunities for optimization thanks to better semantics and better access to native instructions". This seems reasonable on its face, so care to elaborate on the doubt?
The arguments for not switching from C are often performance or target related, so a language that purports to be an alternative to C would want to point out that those issues aren't a problem.
The reasons to switch away from C are numerous and well documented.
> For example, in Zig both signed and unsigned integers have undefined behavior on overflow, contrasted to only signed integers in C.
Huh, that's a strange choice. What's the safety story for Zig? Are these always undefined, or is that only in an "unchecked" build?
The latter. Zig actually kinda does define this behavior as "`a + b` shall not overflow" and inserts checks in debug and safe builds for it. To get overflow, which zig defines as wrapping overflow, you use a different operator and no check is inserted "`a +% b`". For speed optimized builds, unless you've explicitly told the compiler to insert checks in that scope, it will turn the checks into nops.
So, while it is technically correct to say that it has undefined behavior for overflow, the practical reality is quite different.
We do the same thing in Rust, but I think that characterizing this as UB is misleading, personally. We created a new category, "program error", for this, to distinguish from UB proper.
I'm not sure if Zig inherited the defined/implementation defined/undefined hierarchy from C and C++ though.
There's a task to rename UB to illegal behavior: https://github.com/ziglang/zig/issues/2402
As well as some followups to see if Zig can detect all of these errors: * https://github.com/ziglang/zig/issues/1966 * https://github.com/ziglang/zig/issues/2301
Undefined as in undefined by language spec. There are various processor implementations that have different results that are often quite useful. Would you preclude their use?
Right, the key here is that the behavior is defined. That’s why calling it “undefined behavior” is misleading.
It is defined if you want to restrict where your program runs.
If something is defined or not is a property of the language specification, as you stated.
What you’re now bringing up is something different: should a specification define this behavior, or not? I think you’ve properly identified a trade off, but mid-identified the details. Defining a behavior here does privilege certain architectures, but it doesn’t make it impossible. It means the behavior must be replicated in code on some architectures, which is slower.
This is the trade off that Rust (and apparently Zig) are making. This is informed by the architectures that exist, and which ones are wished to support. At this point, two’s compliment is overwhelmingly used, and so the decision was made that defined behavior is worth it. Note the parallel here with POSIX specifying that CHAR_BIT must be set to 8.
Notably, the situation here is so overwhelming that even C++ is specifying two’s compliment: C++20 says that signed integers must be. This was recently voted in. I haven’t read the paper recently and I don’t remember if it changes the UB rule here or not, but it does define the representation of integers, a very similar thing that’s the motivation for UB here.
I was holding out for hardware enforced overflow and underflow checking but I guess it has been abandoned. Thanks for the info. These choices are being made as was made opposite in C standard deliberations... I am interested to see how it will work out.
The way we’re dealing with that is that there’s room in the spec where we’re allowed to turn it to “always checked” as a compiler setting, and will if it ever gets good enough hardware support. We’ll see though!
It's not about the speed being a benefit per-se, but rather not a determent. One of the big reasons people still use C over other languages is that C is perceived to be faster (and indeed usually is).
Whether the claim hold up or not. If you developed a language that's faster than C, would you not advertise that as a feature?
I would, first of all, call bullshit on this claim. A language can't be "faster than C". Comparing a good implementation with a bad C implementation is where these claims come from.
If a language allows you:
- to map cleanly with what compilers know to optimize best
- fine control over stack and heap allocation
- has intrinsics / assembly escape hatch
- allows you to specify that pointers don't alias (restrict in C, or default in Fortran)
- gives you prefetching primitives
You will be able to reach hand-tuned Assembly-like performance (and not just C-like performance).
Case in-point, I tuned my own matrix multiplication algorithms in Nim to carefully control register allocations, L1, L2 and L3 cache usage, and vector intrinsics to reach the speed of assembly tuned OpenBLAS and Intel MKL-DNN (no assembly at all):
bench: https://github.com/numforge/laser/blob/c7ddceb0/benchmarks/g...
code: https://github.com/numforge/laser/tree/c7ddceb0/laser/primit...
And matrix multiplication has decades of research and now dedicated hardware (tensor cores, EPU, TPU, NPU, ...) as this is a key algorithm for most numerical workloads.
> A language can't be "faster than C".
Why not? For instance, with current hardware, the good use of SIMD is critical. If you can't write vectorized code in (standard) C while you can do it in another Language, that would be a "faster" language. Same for other language features that enable faster code: Compile-time computations, better memory model with different pointer aliasing semantic, ...
You can dispute Zig, but in practice there is FORTRAN that is faster for many math procedures.
Yea. It's always funny when people say C is faster than Fortran for scientific computing. I mean, it's true, but only for a very limited amount of people who want to do serious numeric work. C/C++ certainly aren't rare in this area, but Fortran is pleasant to work with for most numeric work and has lots of helpful built-ins and runs close to C in speed.
Looks like its still lacking in network support:
https://github.com/ziglang/zig/issues/2007
I wonder though, does it already have socket support? that could be a stopgap for me. Edit, looks like it doesnt have sockets either:
https://github.com/ziglang/zig/issues/1271
Edit 2, looks like it does:
https://github.com/ziglang/zig/blob/5026db1d/lib/std/net.zig...
https://github.com/ziglang/zig/blob/5026db1d/lib/std/os.zig#...
You can use any c libraries networking or socket implementations in zig but I think thats as far as it goes at the moment.
zig's C-interoperability is second to none and using libcurl works great for networking.
example: https://github.com/donpdonp/zootdeck/blob/master/src/net.zig
Nim and d both have comparable interop, and nim even works with c++.
To be fair, Zig's C interoperability appears to be inspired or borrowed from Swift's little known secret called the ClangImporter.
Can you though? I cant get cURL to work:
After a few minutes of trial and error (mostly around convincing Zig's type checker that various things are indeed enum members, and fixing cases where the C version outright ignores return values), I was able to produce a working example:
Buildable with "zig build-exe curl-test.zig --library c --library curl" (I have to add "-isystem /usr/include --library-path /usr/lib64" to the end of that on Slackware64 14.2; YMMV). Should produce a runnable "curl-test" (or "curl-test.exe") binary executable in the current dir.// curl-test.zig // Direct hand-translation from curl's simple.c example usingnamespace @cImport({ @cInclude("stdio.h"); @cInclude("curl/curl.h"); }); pub fn main() anyerror!void { var curl = curl_easy_init(); var res: CURLcode = undefined; if(curl != null) { _ = curl_easy_setopt(curl, CURLoption.CURLOPT_URL, c"https://example.com"); _ = curl_easy_setopt(curl, CURLoption.CURLOPT_FOLLOWLOCATION, c_long(1)); res = curl_easy_perform(curl); if(res != CURLcode.CURLE_OK) { _ = fprintf(stderr, c"curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } curl_easy_cleanup(curl); } }
https://ziglang.org/documentation/master/#Zen >> Minimize energy spent on coding style.
For me this is sort of important because languages not having standard coding style end up 10000 coding styles. Rust fmt is an excellent example for avoiding that. Does anybody use Zig + fmt?
I know they have something. https://github.com/ziglang/zig/issues/1523
There's a section in this notes about it https://ziglang.org/download/0.5.0/release-notes.html#zig-fm...
The end even has a link to a web-based version of it.
Thanks, I totally missed it.
It's an exciting week for programming languages. First Nim, then Zig. Keep up the great work!
Does anyone here have a good understanding of the relative merits of Zig, Crystal, and Nim? Especially compared to higher profile languages like Rust and Go?
All of these languages seem to be statically compiled, and they all seem to promise both performance and safety. Rust and Go are discussed all the time on HN, so I expect most of us here have a reasonable notion of their relative benefits, for example, performance compared to ease of learning.
What about these lesser discussed languages Zig, Nim, and Crystal? Are their runtime performances comparable to one another? Are they equally safe to program in? What are their relative strengths? Or does it just come down to syntax preference?
I think there's value in having a lot of choices to program in, so I'm not asking which language is "best" any absolute sense. We're better off for having all of these languages coexisting. I'm just asking if anyone with experience in the three of them has any insight or impressions of their relative merits.
Zig and Rust both don't have a runtime/GC, they should be on par with performance in C, meaning they are slightly faster than the others (of course this all depends). Go is the only one you listed that doesn't have some kind of parametric polymorphism support (generics). Rust is the only one that will validate your program is memory safe at compile time, they call this 'data race freedom'.
I think in the end it's really up to your tastes which you prefer. Have a look at the tenets of each of the languages and see which resonates with you.
I didn't try Crystal, but Nim is definitely on par with C.
Actually you can use just plain (stack) objects and ptr object and you have no GC involved.
Pass the flag -d:useMalloc (or just call your preferred allocator) and you have the full C experience and speed.
Maybe, but libraries that you use in Nim will probably use the GC, so will many functions in the std library (I imagine). Additionally, the binary you ship will still probably include the runtime & GC. So it's not really fair to say it's on par with C/Rust, there's reasons to avoid GC besides just performance. Say you are writing an embedded application for example. I don't know any Nim, so correct me if I'm wrong.
> not really fair to say it's on par with C/Rust
It is. The GC is inline, thread-local and can be run manually, disabled completely, or switched to the new owned reference.
Having looked at all three, I've so far settled on Zig for a couple reasons:
- Cross-compilation is trivial (I haven't dug too deeply into the cross-compilation story on Nim and Crystal, but Zig's is front-and-center)
- Allocations are explicit (functions that require allocating memory do so by accepting an allocator as an argument)
- Windows support seems to be better than with Crystal (which means I can use Zig for desktop programming for my dayjob, since at work most of the PCs here run Windows 10; not that I would at the moment, given that it's very much not production ready yet, but still)
- I strongly dislike Python's syntax, and therefore strongly dislike Nim's syntax (I also somewhat dislike C's syntax - and therefore Zig's - but not nearly as strongly)
- Zig's build system is pretty great (albeit not exactly intelligent when it comes to finding system header and library paths, I've noticed)
That said, there are some downsides to Zig that I'm hoping can be resolved (whether by someone else or - if I get comfortable enough with it - myself):
- Profound lack of standard library documentation
- I like Crystal's syntax better
- C++ interoperability is practically nonexistent (though it might be possible to write small C++ shims that link against Zig-outputted headers; not sure if Zig's included C compiler can compile those, but if it can, then this would be easy to integrate into the build process)
To be fair, C++ has no ABI, de facto or otherwise. The best way to write C++ that can be called by other languages is with extern "C"
That is true. I only brought it up as a comparison with e.g. Nim, which does (claim to) have the ability to interoperate with C++ directly without C-ABI shims. D supposedly has similar capabilities, as does Chicken Scheme.
But yeah, the use case ain't to work with C++ code I've written (if I'm writing it, then my goal is to write as close to zero lines of C++ as possible ;) ), but rather to work with third-party C++ codebases that do not expose a C-compatible API (or do so poorly).
One significant difference is that Zig has no GC, whereas Crystal and Nim both do.
I don't know about Crystal but Nim GC is optional and even on a per-type basis:
Use plain object and you are on the stack Use ptr object and you can use any allocator you want (Nim's default, malloc/free or jemalloc, mimalloc, ...) Use ref object and your reference will be managed by the GC.
In terms of GC, you the default is deferred reference counting (no ref counting if object is created and destroyed at the end of the scope). It accepts real-time and max-pause tunings parameters, to allow it to stay beyond those 60FPS / 144 FPS requirements
You can also choose java-like mark-and-sweep, the Boehm GC, the go GC, or no GC and get a warning everytime you try to use a type that uses the GC.
Sorry! But Crystal was last week's news!
This week is Zig's turn on HN :)
To be fair, Crystal's release was the same day as Nim!
But yes, now it is Zig's time! I'm happy for all of them.
I wonder why scrolling is broken once the page fully loads.
Seems to be fine on Firefox (both with the mousewheel and arrow keys).
The new async/result location stuff reminds me of the Pin concept that Rust fairly recently adopted. It seems like a pretty huge step forward for the language. Especially since it's casually mentioned that it also solved "safe recursion".
I'm curious about the tools zig offers around doing asyc work while guaranteeing certain invariants, and how the @Frame builtins work with that, but I'm guessing that's going to be elaborated on in a future post.
The author also livestreams (I believe on twitch) and publish VODs on youtube, showcasing new features, updates and whatnot!
I'm quite enjoying watching progress updates, relevant examples and Q&A :)
The streams are on Twitch: https://www.twitch.tv/andrewrok
Looks promising.
Can we have a consistent function notation
fn name(a: int, b: int) -> void {}
This also helps with clean functional type arguments.
swift and rust support this.
A single consistent notation will be helpful for new polyglots.
Zig, Crystal, Nim, Rust, Go. It seems like the industry is responding to the end of moores law and is refocusing on performance. Contrast this with 90's languages like python, ruby, java and c# where computers were expected to get exponentially faster and they focused on developer ergonomics instead.
Can anyone familiar with zig tell me how well it works with more macro heavy libraries? Something like GTK (https://developer.gnome.org/gtk-tutorial/stable/c450.html) or something with generics.
The windows build isn't working for me. Still stuck on 0.4 .
At the very first glance: what is wrong with
function xxx()
instead of fn xxx()
what are we trying to save here?Also would be nice to do import instead of @import. More readable I think.
Afraid to dig deeper as I do not have time to play with the languages that I can not yet use in production.
The @ indicates that the function's built into the compiler itself. Yeah, it looks ugly, but it's nice to be able to visually identify "okay, yeah, this didn't come from any of my imports", and also (I'd assume) avoids cases where one might inadvertently shadow them with their own function definitions.
Re: fn v. function (v. fun v. def v. defun v. defn v. define v. proc v. sub v. label v. the cornucopia of other options here): I don't think it's all that big of a deal. fn does happen to be short, which means more actually-useful information can fit into the same line, so I guess that's nice. The choice of keyword for function definition feels like a really weird criticism.