I just upgraded some code to Zig 0.16.0 and I am actually really happy with the results. It impacted A LOT of things, but the changes were actually very good and seems to have set the language for a bright future, especially with the new IO mechanism which allows supper efficient code that looks good whether it's implemented single-threaded, multi-threaded or just via an event loop!
If you haven't tried Zig since 0.16.0 was released, I highly recommend having a look. The release notes for this release were huge!!
the “(super) efficient” is not there yet. Io is still dynamic dispatch with multiple layers of indirection. afaik it’s slower than before.
the upcoming releases are expected to provide a solution to this “dispatch is comptime-known, but still dynamic” problem, and drop the loses in efficiency.
Hmm in the 2025 talk ( https://youtu.be/f30PceqQWko?si=qZESxMaSyt7fYMfz ), Andrew emphasizes that this approach is more efficient than before- even showing compiled assembly iirc. I guess that was a one-off?
A vtable indirection is essentially free when you're going to perform a syscall. What matters is that the buffer is above the vtable (which is already the case for the current implementation) so that you don't pay for the indirection when hitting the buffer.
It's not just I/O, it's also mutexes, condition variables, time, etc. It's not horrible, but it does add up, so calling it super efficient is a stretch.
A modern allocator with per-thread cache can satisfy some allocations in 20-30 cycles - dynamic dispatch can easily double that, even if the target is still in cache.
It's one of these things where it's extremely use case dependant - like many performance issues, you probably don't care about it - but when you do it matters.
Inderect call cost is a few cycles, if predicted. Now, you can argue, that it may be mispredicted and misprediction would cost about 20-30 cycles. But if it is mispredicted, then you are not calling into allocator often enough. And if you don't hammer it hard, then why do you care about preformance?
I believe their plan is using "restricted function pointers", where you can specify that a pointer will only ever be to a function defined in the codebase. I'm pretty sure they also have plans for devirtualization, but I haven't followed super closely.
Oh, is it not a specific keyword? I thought they were thinking of it being a keyword so you could be sure that it was restricted, in case a variable or function was exported that took in a foreign pointer.
> especially with the new IO mechanism which allows supper efficient code that looks good whether it's implemented single-threaded, multi-threaded or just via an event loop!
I had some trouble understanding how the async/await mechanism works:
var foo_future = io.async(foo, .{args});
defer if (foo_future.cancel(io)) |resource| resource.deinit() else |_| {}
var bar_future = io.async(bar, .{args});
defer if (bar_future.cancel(io)) |resource| resource.deinit() else |_| {}
const foo_result = try foo_future.await(io);
const bar_result = try bar_future.await(io);
My assumption is that calling io.async using an event loop implementation of IO, it will internally start a "task" (or whatever it should be called) and that the future is a handle to it. So far so good.
The part that I don't understand is what happens when you call future.await(io). Will the IO implementation somehow suspend the current function and resume once the future is resolved? If so, does that mean that every function in zig is a stackless coroutine?
> If so, does that mean that every function in zig is a stackless coroutine?
No and yes.
If you're using Io.Threaded, then the concurrency model is multithreading and calling Future.await will block your thread on a OS futex.
If you're using Io.Evented, then the concurrency model is green threads / fibers and calling Future.await will suspend the current green thread by yielding (swapping CPU state with another fiber).
Zig currently does not support stackless coroutines so today you can't have that, but we used to have them (pre self-hosted compiler), and there's an accepted proposal to bring them back, in which case any function that calls await, or that otherwise has a suspension point, would have to be transformed into a stackless coroutine by the compiler, yes. The plan is for that to happen transparently without requiring an `async` annotation in the function signature, like we already did in the past.
> there's an accepted proposal to bring them back, in which case any function that calls await, or that otherwise has a suspension point, would have to be transformed into a stackless coroutine by the compiler, yes. The plan is for that to happen transparently without requiring an `async` annotation in the function signature, like we already did in the past.
If the compiler will treat functions that call a library function (Future.await) as special and change how the call site is compiled, why not just have an `await` keyword that when present will convert it into a state machine that can be suspended/resumed?
In other words: What is gained by not having a keyword that changes how a function is emitted if the compiler will change it anyway based on detection of a library call?
It's not Future.await that is special per se, it's that it (Future.await) will have in it somewhere either in its body or in another function that it calls in turn, a use of `suspend` (using old Zig syntax).
`suspend` is the keyword that best fits your description (you can think of it as being closely related to `yield` in other languages), and it's kind of a lower-level primitive compared to async/await.
This also means that, according to our plans, Zig will have to propagate "stackless-ness" upwards in the call chain while analyzing the code (thus making Future.await not special per-se).
> This also means that, according to our plans, Zig will have to propagate "stackless-ness" upwards in the call chain while analyzing the code (thus making Future.await not special per-se).
Very interesting, will be following Zig development more closely. Thanks for sharing!
Maybe one day it'd be possible to use these new features, but I find myself using `.use_llvm = true` in my zig builds for stability and lesser tested targets.
After having used Zig for a couple of months now I am convinced it is a fantastic tool language. You just pick it up to hack some idea together freely. Every time I hit a wall, I find the creators have thought of it already and offers comfort. But nothing gets in your face how to use the programming language "correctly".
For me it is now the go-to "tinker in my garage" language.
Ugh. This is something I hate about Go. I would be happy to have unused variables generate warnings, but as errors, they turn the toolchain into an adversary.
It's common for me to temporarily comment out a variable's use when developing new code, as I experiment with ideas. It's even more common when working in unfamiliar code, such as tracking down a bug or incrementally adding a new feature. It's an important part of my exploration process.
When I hit Compile at that point, I expect the compiler to build the work-in-progress exactly as it is in that moment. Executing the output allows me to spot check the snapshot's behavior against my expectations and mental model. The compile step also assures me that no syntax errors have crept in while I was focused on the logic flow or general shape of the code.
When a compiler refuses to do its job, and instead barfs up spurious errors complaining about unused variables, it brings my workflow to a screeching halt. In order to make progress, I am forced to abruptly leave my current context to visit all the different places where those variables are introduced, edit them, try again, discover that those forced edits have left more variables unused, and repeat the process until the combative compiler shuts up and does what I told it to do in the first place. By the time I'm allowed to return to what I was doing, my train of thought has been derailed, the bits of logic that I had been juggling have fallen to the ground, and my focus destroyed. And then, once I have recovered my original thoughts and seen the output of my snapshot build, I have to go back and revert all those forced edits before I can resume my work.
What an aggravating, disruptive, and completely unnecessary waste of my time and attention.
I hope Andrew has the good sense to let errors of this kind be silenced or demoted to warnings, perhaps with a compiler flag or debug build mode.
Yeah it drives me insane. Working in any ide's golang lsp/plugin that tries to solve this for you every time you hit save also drives me insane. Feel like something is lost experiencing/learning/becoming masterful at a language when these types of decisions get automated while you're hand crafting.
I actually really valued my exploration into C where if i did that, id get a fat warning on compile, but I could still proceed to test my stuff. When I'm ready to commit to some cleanup efforts which is usually a headspace-thing, then I go cruise around and cleanup unused variables.
Well its also just idiotic in terms of how some huge number of people write code.
I know I'll need so-and-so import, so i write it at the top of the file before i even start, I press ctrl-s and it dissapears... I honestly laughed out loud the first time i used gopls.
I love Zig and I am generally very happy with Andrew's benevolent dictatorship and the benefits of having one single smart tasteful person in charge of decisions, but the unused variable one really hurts. My guess is that he's seen what a mess C code can be with regard to warnings and so is just totally unwilling to compromise by adding the concept of warnings to Zig. But if I had one wish about the language, it would be for a command-line flag to disable unused variable errors. So much effort has been put into making iteration fast (all this build system stuff, the custom backend, incremental compilation) and then there's just this giant blocker preventing fast iteration on the editing side.
The converse is that if you have many unused variables some of them are used in some contexts but not others and then when you use FIND to find occurrences of a variable you will find many which are not relevant and some which are.
Overall the code gets smaller and easier to understand when it only has things that need to be there. If you comment out a an unused variable you can see from find-results that in effect that occurrence cannot matter.
Unused variables are "noise" that hide actually important things.
If they wanted the release build to be an error I wouldn't care. Having the current solution be "have the editor automatically change code to include or remove the underscore" is so wrong to me. Just invented a problem that needs tooling to modify source code to fix.
So your ice turns 'let unused = func();' into 'let _ = func();' automatically? Okay but then do I later have to grep for 'let _' to find all these unused variables and delete them? This is silly and a has consequence of zig not having any system for warnings
as a fan of him this rationale did not make a shred of sense
"Both parties can have their preference" - this was already true of C where you can just turn enable unused variable warnings or turn them into hard errors, this has nothing to do with the decision.
I also don't want my lsp randomly rewriting my code (???)
In rust, having unused variables as a warning (but not an error) let's you refactor code, test it and see what is now unused as a result. You can then remove the unused items. Zig requires you to remove the unused items (e.g. with '_ = ...;' which is then something you might forget about) before testing, increasing friction.
Multiline comments are less important, but its still convenient for commenting out large chunks of code. IDEs make this a bit easier when you can press e.g. Ctrl+/ to comment out the selected lines with //, but it doesn't work in all cases.
The friction stops zig from being fun imo. A shame because I really like comptime.
Thanks, there is always a risk of people jumping on replies like that to scream that you are doing it wrong. I just wanted to see how other people do stuff. Hope everyone stays nice.
Not the person you replied to but I leave unused variables as future TODOs. It's a warning in F#. I also often use them for inspecting data in the debugger
My go-to "tinker in my garage" language is Python - lightweight syntax that stays out of your face, batteries included, packages for everything that's not included. What's Zig's edge?
Have you ever thought "Ugh, this bit of Python code is running much slower than I expected on my computer. Wonder if anyone has written a native library for this"? That's probably the closest use case for someone who matches your description -- a language that is much more ergonomic, much more 'modern' feeling (in all the good ways), while still extremely compatible with C.
As for the language itself, it's going to be more verbose than your Python code. Cons: you'll have to spell out a lot of things that you thought were obvious assumptions. Pros: you will be able to look at a page of code and know with a great degree of certainty that there are no hidden gotchas. No monkey patching, no __init__. Basically, it just does what it says on the tin.
And finally, about the std lib and batteries: there's HTTP(S), compression algorithms, hash algorithms, RNG, I/O, the basic data structures you'd expect, JSON. Third-party libraries, if you choose not to vendor, are handled by including the repository url in a file (also automated by a CLI command), and then adding it to the build script (not automated). The `zig` command handles fetching and ensuring sanity, but otherwise assume a bit of elbow grease will need to be involved.
That's actually a great argument for Nim[0]. Easy interop with C, native-speed performance, and a syntax very close to Python in both readability and how quickly you can get something working.
Batteries included, automatic memory management without a conventional GC and metaprogramming - is a really cool combination.
Aggh if only its LSP was better! I have always run into issues when using Helix with it (it kept crashing), and I'm absolutely spoiled by good LSPs in other languages :(
Wish I had the time and skill to actually contribute to the LSP, if you have ever used Nim it's a seriously underrated language.
Araq the Nim creator is working on a rewrite of the Nim compiler called Nimony that'll become Nim 3.
It's making fast progress and will provide the basis for a proper LSP! Nimony already supports incremental compilation and parallel which are key pieces for good fast LSP.
it is my second choice next to Zig and does have a lot of cool features, for sure.
The nice thing is that all these languages feature easy C interop so you can use a C FFI as the interface between them if you want to experiment with, for example, writing a module in Nim
Rarely. Most tinkering tasks just don't have enough heavy duty computation in them to as much as strain a modern CPU. And most of the rest are covered by packages like numpy or pytorch.
For the rare exceptions, I make a C lib and call into it to get my numbers crunched. I get that Zig is a viable replacement for C there. But I don't see it replacing Python.
And if you really need more performance (or, more often, fast startup times), Go gives you 90% of the speed with 30% of the effort. Rust if you really want to squeeze everything that can possibly be squeezed of that CPU.
Technically true, but unsafe Rust is much harder to deal with because it makes assumptions (sometimes very non-obvious) regarding aliasing that Zig does not. You might think you can avoid problems by using T* rather than T&, but every accessible local is effectively a T& so it's still very easy to do something you're not supposed to.
No, I'm not sure where you heard this but this is certainly not the case. If I have an i32 on the stack in Rust, there are no aliasing invariants that need to be upheld. You do, in fact, avoid problems in unsafe Rust by just using raw pointers instead of references. The idea that "unsafe Rust is harder to deal with than {Zig, C, C++}" is a long-outdated notion from old versions of Rust, before there was a dedicated way to produce a raw pointer without first producing an intermediate reference.
Go does not give you control. Much of the speed comes from choosing the right algorithms - including memory management. In Go you have GC, you don't have that in Zig.
Linked lists are a lot less slow if you use Arena allocation around your hotspots and make sure to allocate space for as many as you thing you need, since they will be carved out of a contigious block of memory and will stay in CPU cache.
Golang also requires you to write more code, as it lags Zigs try operator.
that’s not what the benchmarks say about Go, and based on multiple reports, Rust does not scale well into large codebases, which eventually become brittle and very difficult to change
Zig is a return to “no magical effects,” except with reasonable safety
I would be very surprised to see a large Rust codebase being harder to maintain than a large Zig codebase. The former makes it much easier to maintain invariants at scale.
Well, you could go ask Richard Feldman, who I believe cited that reason to rewrite the nascent Roc language from being implemented in Rust to Zig, or anyone else who is moving from Rust to anything else. I've seen multiple people at this point complain about the scaling issue with Rust; the larger the codebase, the more you end up fighting the compiler before anything will actually build.
Note that it doesn't matter if the compiler is correct about its claims; if the language doesn't actively discourage patterns that produce this outcome at scale, then the language does not scale, end of story.
The trend is basically either linear or exponential: as more LOC of Rust are added, the greater the percent of total time you spend fighting the compiler to get a successful build, especially in a team context (which is exactly what gets you to >1M LOC). Solo devs can contain the whole design in their minds and may not run into this issue as much; the problem specifically occurs on teams where the mental model MUST be fractured by necessity, and this results in "distributed knowledge of magic" that ends up constantly breaking.
Perhaps this explains WHY there aren't that many Rust projects done by more than 1 developer that approach that many LOC.
Unless you enforce those macros somehow in a team setting, someone's going to forget to use them, and then you're still stuck with the original problem.
By the time C++ and Java were as old as Rust is today there were thousands of programs that over 1MLOC that had been maintained for at least five years. Rust is a rather old language, yet I doubt there are even hundreds of Rust programs over 1MLOC.
These reports are smoking crack. Rust scales gloriously well into large codebases, and it especially shines when it comes to making major refactorings. Please don't bother speaking about things that you don't understand.
You are entirely right here, you're also incredibly rude. Please don't bother replying when the only thing you're actually doing is being condescending and spreading negativity
Rudeness is perfectly acceptable when it comes to preventing the spread of blithe and thoughtless disinformation. I have no obligation to be polite to people who speak authoritatively on topics they know nothing about. I recommend you spend less energy on trying to defend clueless people by policing the tone of the people educating them, unless you think that polite ignorance is more societally valuable than brusque truth.
Could you please not sermonize or act like a demanding customer? We don't, and can't ever, see everything that's posted here, and even if we could it's not appropriate for moderators to adjudicate on the correctness of any claim. It’s by cultivating discussions between different people with different perspectives that we illuminate topics here. Obviously you have deep expertise on those topic. Great! Please educate people rather than berating them.
This is only a place anyone wants to participate on because we have guidelines and others make the effort to observe them. You've been here long enough to know that. Please don't trash what you seem to have found value in for so long. We don't need you to be "effusively pleasant" or erudite, just respectful.
For the price of all this righteousness you could have provided a reference. Some reference. So that curious bystanders like me can learn from the exchange.
The argument is that the ergonomics of using Python are worth the squeeze of learning two languages. Are the ergonomics of using Zig really enough to justify replacing Python on the happy path, or would it end up replacing just C?
I find Python extremely unergonomical. Sure, it's nice to read (pseudocode, yay!) and sure, its library ecosystem is beyond amazing.
But... the LSPs I've tried (and I've tried a bunch!) are all atrocious: false positives and false negatives galore. Perhaps I'm spoiled by LSPs from languages with better type systems. Our code is strewn with (to me) mysterious comments such as `# NOQA 1234` which my colleague uses to make his Python tooling work with the codebase. I'm used to languages like Elm or Gleam in which a LSP error means there is an actual problem with your code, and a lack of a LSP error means the code will compile and run.
Even if you are fine with Python's speed, its memory consumption DOES effect things and can be an extraordinary pain when you need to fit the result of your tinkering in any sort of constrained environment.
By the time I’m memory constrained even on my laptop the processing cost of whatever I’m doing has gone beyond shoving it in the first scripting language I can find. Every device I write code on has at least 16GB RAM - most of them are 64 or 128
Not to mention that where heavy computation is required, Python often has libraries that are much, much faster than anything you can quickly hack together in C or Zig.
Only if you doing something thousands of people has done before.
Anything new, even very simple and you are on your own and Python is 100x slower than naive C implementation on many tasks.
Last little project I remember is writing a solver for a puzzle game my friend published. Python just doesn't work at all for such tasks.
I think you are wrong about speed of those libraries as well. In my experience naive code designed for a specific task beats highly sophisticated general code and it doesn't take a rocket scientist to get huge speed-ups over some well established fast library.
I don't think performance has got much to do with tinkering. IMO the real benefit of Zig is you get the flexibility of C with the ergonomics of a modern language.
I like Python as a tool language, and I am very impressed by projects like Micropython, but you always eventually run into a wall. I.e. you are never going to write a compute shader in python, but I assume someone is going to try.
I think the programmer should meet the hardware in the middle, and Python has a few too many layers of indirection to do this well.
> I don't think performance has got much to do with tinkering.
Yes, in general, but also there are cases when you realize you can, idk, parse a CSV file in 0.2 seconds instead of 200 seconds. That kind of improvement unlocks a new level of tinkering.
Zig is low level, so it will certainly not replace your python usage, it is more like a modern C than anything else. There’s a video of a recent interview with Andrew Kelley, if you want to watch it to understand better what Zig is for, it’s on Jetbrains YouTube channel.
No, I get that, but Zig being low level is kind of why I don't get why it would be a good tinkering language?
When I want to tinker, I just want my logic to work, first of all. In 9 cases out of 10 that means going for high level. Even if the resulting code works with low level things like binary structures.
Low-level programming gets a bad name because C has many footguns and the spec leaves much behavior undefined - a fact that implementers use almost adversarially (which I'd support, if the goal was to refine the spec...).
C++ adds more high-level conveniences without actually removing the footguns and undefined behavior (much C code compiles in a C++ compiler).
Zig tries to keep the low-level C philosophy but have things more well factored and well defined. The result is you _can_ tinker in high-level code, yet "drop down" into low-level code as you desire.
(Compared to rust, you get fewer compiler-enforced guarantees, but unlike C the language isn't trying to make high-level code adversarial).
It made me laugh to think of C implementers being adversarial! It can feel that way.
I haven't really used modern C, not sure if it's evolved as much as modern C++, which I feel is a joy to use, and a lot safer. But then I've been writing C++ for decades.
I feel like C evolved from basically syntax sugar for assembly, so that's where all the footguns come from, rather than being actually adversarial.
If some of the things that the C standard left undefined had instead been made implementation defined then the compiler would at least be obligated to do something that makes sense on the target architecture, rather than having license to take the lawful-evil route. (Plenty of architectures have addressable RAM at location zero, for instance.)
For some reason this always brings to mind that moment in Red Dwarf where Kryten, devoid of his behavioural chip, deems it appropriate to serve roast human to his crewmates. "If you eat chicken, obviously you'd eat your own species as well, otherwise you'd just be picking on the chickens!"
Both C and C++ compilers (in fact, they share this part) very aggressively exploited undefined behavior for performance. But I this was certainly not adversarial. Programmers also regularity picked optimizations over safety. I think nowadays the unsafety of C with modern tooling vs the safety of - say - Rust is very much exaggerated.
I would add that Delphi still follows along, enough for an yearly conference in Germany, and that C# since getting Native AOT and the low level programming improvements, is close enough to Modula-3 design.
There is Swift as well, although quite far from Wirthian compile times.
Almost all of my tinkering is “download this thing, cache it (because it’s huge), run a program or a series of programs on it, and package the output up somewhere.” When I’m writing the thing that does the work I’m not tinkering any more..
I've been places, from embedded bare metal to ML AI, and that "embedded bare metal" end is the one place I don't use Python directly in. Embedded bare metal is just ruled by C forever.
Bit of a shame, because C is kind of bad at its job, but nothing else has the "compatible with everything" badge of honor.
The tooling around embedded devices though? Python.
When I want to tinker it’s usually because I want to make something faster than anyone else has done. Does that help illustrate why some might prefer to tinker in Zig, and why your definition of tinker seems a little narrow?
Most of the time "make something faster than anyone else has done" is just not worth doing? Good enough is good enough. Unless it's some super hot path and it's the speed that's the main goal, nothing else. Which is rarely the case.
If you only ever think of tinkering for the purpose of execution speed ninjutsu, isn't it your definition of tinkering that's far too narrow?
No, I’m saying that it’s how I like to tinker. Others have their own ways of tinkering that are just as valid!
I personally think the optimization challenge is fun. I like digging in to low level stuff, reviewing the assembly dumps and processor pipeline architectures. I fail or give up most of the time, but I enjoy learning in the process.
I’m just trying to show how Zig fits my tinkering well, since you said you can’t see how Zig would ever be a good fit for tinkering. I’m not saying it’s a good fit for all forms of tinkering.
Tinkering means different things to different people! Want to tinker with your hardware, as bare metal as possible? Or extract every inch of performance out of your CPU? Zig is great for that.
And not only that, if you're doing something in Python, somebody has done it before. Maybe not this exact thing, but something close enough to it. LLMs know it, Stackoverflow knows it, whatever esoteric protocol or file format you're trying to interact with, somebody wrote a library for it in the Python 2 days and has ironed out all the bugs since.
There's no other language quite like Python in this regard. Typescript is a close second, but the lack of metaprogramming facilities, no access to the type annotations at runtime, and the lack of operator overloading make some things needlessly complicated and uglier than they have any right to be.
At university we didn't use Python, but mostly Java, C++, C a bit of Haskell and some even more esoteric ones.
Sure thing I got exposed to Python, but to this day, I don't like it.
All that whitespace typing just kills me xD
I am not normally someone who cares the slightest about Syntax.
I've written Rust, C++, C, C#, JavaScript, TypeScript, Haskell, Kotlin, Scala, PHP, and many, many more in my spare time and being paid to... Yes, I've also been paid to write Python and I use it for some scripts instead of bash occasionally, but boy will I never like Python
Well, I feel you very much about Python! Also was coding in plenty of languages for past 30 years (assembly, pascal, C, D, java, visual basic, typescript, C++, and so on and so for) and every now and then I wonder if that aversion of mine towards indentation based languages (python, nim) comes from lack of enough flexibility of my mind/aversion towards training my brain to use and recognize different coding way or it is simply because it not ergonomic enough once you reach certain skill level.
I once had to write software that would go through whole bunch of PHP code (more than 5000 files), parse / discover certain patterns and write report with proposed fixes or same but with the fixes applied.
For whatever reasons I had to do it in Python. It was total nightmare to debug as the execution speed in debug mode was insanely slow.
I could've written it in C++ in exactly the same time and not to have any of the performance problems.
The only language I've historically been able to claim to know without feeling like I'm straight up lying has been Python, and having got past my first maybe 1000 lines of Zig I can say pretty confidently that whatever magic makes Python feel comfortable to write, Zig has too.
It requires more of you in some ways, notably that you have to understand the basics of memory management and the behaviour of the stack, but so far I've found the affordances that the language provides for handling this stuff feel very intuitive.
The only sharp edges I've felt so far have been the sometimes hard to guess locations of things in the standard library, and the permenant anxiety that arises from knowing I'm going to be a few more versions behind the current release with every month that passes.
It's true that Zig is very readable. I haven't yet seriouly studied or written much of it, but browsing through codebases of popular Zig projects, a lot of it just makes sense intuitively. In that way it has a Python-like friendliness of syntax.
I enjoy the community and culture around Zig too. The other day I found a forum thread where people were sharing what they're currently building, and there were so many fun projects from small hobbyist things to large ambitious ones. For the latter, the main concern is the stability of the language, but the good thing is that everything is out in the open, everyone knows Zig hasn't reached version 1 status - but I can see concrete steps are being made to find a good solid interface, including this I/O stuff in 0.16. As someone casually learning the language, I find it refreshing to have insight into the development process.
I like that you have more freedom. You can play around with some idea but once you want to do something "serious" you can break into it directly. I start simple but sometimes blip into some performance obsession and I find Zig allows that.
took me a minute but once i started tinkering with compiled outputs and not dealing with runtimes and venvs my ability to tinker went super crazy. being able to pass something off to my vps with no dependencies if i was tinkering with something hosted was fantastic, or sharing a sidequest with a coworker that's buttoned up and ready to go with no additional environment setup for them
I won't call it "opinionated about how to use the language correctly."
Space is valid and it compile, Tab don't --- that's it.
When one say "opinionated about how to use the language correctly", I would think JavaScript with or without end of statement semicolon and being yell at even when your program works.
I have a lot of respect for Andrew, and I really enjoy Zig, but God that interview was awful. Andrews answers were fine, but the whole thing felt very sycophantic.
There's a lot of little things that just added up, but I think the clearest example was the exchange regarding Andrew's pay from the foundation. I don't have an issue with him being paid for the work, or the amount he's taking, I would be surprised if anyone really did, but the way the interview asked the question came off as "Oh you're only making x? You should be paid so much more", and Andrew even commented in it saying "...it sounds like you're implying I deserve more...". I'm of course paraphrasing, but that's the impression I walked away with.
The questions also had a tendency to be somewhat shallow. There were a lot of places where it felt like the interviewer was queuing Andrew to respond to criticism or explain controversial choices made for the language or tool chain, but the interviewer doesn't really follow up on them are point out what the issues might be.
It might have been expectations I guess. I was hoping for an interesting technical interview and instead it seemed like a fluff piece.
I wanted to try zig, but the language still moves way, way too fast, they break API for every release, so i simply couldbt keep up, while learning the language/debugging the build system/trying to actually implement what i wanted
After watching the Jetbrains interview, I still want to try again but maybe i ll wait till the 1.0 is out
My kingdom for Zig to have an official mechanism to emit the Linux library stubs.
Zig’s ability to crosscompile and target arbitrary versions of glibc is PURE MAGIC. I leverage this magic in an unrelated C++ build system. But I have to hack around to get those library stubs from Zig. Would love it to be an official output.
Zig is also a god send in that it has all the generated glibc header bullshit for Linux.
I've got a system that is able to cross-compile ffmpeg for Linux from Windows. Which is a shockingly painful and rare capability. Linux userspace design is so so so so so bad. So embarrassingly bad.
In my experience, this (for now) is mostly aspirational. It's obviously a major goal, and there are clear milestones outlined on how to achieve it, but in practice the initial compile of an empty project or the excruciating pause when you `direnv allow` and ZLS needs to be (re)built are not what I'd describe as "terrific".
What, you wouldn't want a condescending lecture on which exact, unpopular even at the time, languages from the 80's, which had 10% of features of Zig and ran 20x slower, also had fast compilation times? For the 1000th time? Shame on you.
And running `zig test file.zig -OReleaseSafe` takes a couple seconds on my computer. It also keeps taking the same amount of time every time I do it when I modify that file. Using 0.16 (or master) version so my toolchain isn't old and I'm on linux.
Zig is super nice to use as a language but the compiler/stdlib isn't developed conservatively enough imo.
You may not run into an issue like this when starting out with hello world but you will want to run optimized binaries for fuzz testing or benchmarking. And then it becomes super annoying considering you might be compiling a relatively small amount of code.
Just for comparison, imo Zig is a much better language than these other languages, but if you did something like this in Rust/C++/C etc. this kind of issues basically doesn't ever happen. Assuming clang/gcc/ninja etc. usage for C/C++.
I am able to use Ninja/Python/clang for configuring/building (-O2 or -O3)/testing ~10k loc C++ project in 200ms on the same computer for example.
> And running `zig test file.zig -OReleaseSafe` takes a couple seconds on my computer.
What kind of computer are you on? I just ran that test (latest master build, first run):
~ % time zig test file.zig -OReleaseSafe
file.zig:1:17: error: expected type expression, found '{'
if (2 * 2 != 5) { @panic("fail"); }
^
zig test file.zig -OReleaseSafe 0.03s user 0.44s system 505% cpu 0.094 total
Granted I'm on an M4 Mac but I wouldn't expect another system to be 20x slower.
Also another point. I have some zig projects from couple of months back and they all have build.zig/build.zig.zon. I tried to copy that to the new dummy project I did and of course the API changed and that is broken now (just 15 lines of build.zig code).
This kind of thing just feels unacceptable considering I don't really see ANY improvement on the issues I had from back then.
Also had a similar baffling experience when I last tried to come back to writing Zig. The std.time.Instant or similar API that also exists in rust and most other languages was move to the new Io interface and they also completely removed that std.time.Instant code.
Overall it feels like people developing the tool don't respect the people using the tool. C++ or even Rust are much less enjoyable languages to write compared to Zig so it is really sad that it is not possible to actually use Zig for me.
Pascal has a lot of influence on this, but Go as well! My PL friends often talk about Go's benefits and flaws when thinking about advancing other their own projects or improving the mainstream languages they work on.
Then you haven't been paying attention to the space. At all.
Every single new post-Go language, like Rust & Zig, ships with an opinionated autoformatter and a single go-to command to manage builds, packaging, etc.
And how many new languages do you see include inheritance and exceptions?
Older languages have started adapting what they can too:
- Python with `uv` and `black`
- Java with goroutine-like fibers and modern configuration-free gcs and the go-gc-like low pause times zgc
- Many languages now ship a production-ready http server, just like Go
Go changed something, not sure if 20 or 21, where it will download the Go compiler of all your third-party which don’t match yours. It slows things down.
There is an idea I've been kicking around for a long time, which I'll just call dual programming. The idea is to develop a stack that consists of just two programming languages, 1 higher level language, and one lower level language. You are supposed to do as much programming as you can in the high level language, and only drop into the low level language as needed. The problem is that unless you already know a low level programming language really well, you'll most likely have to re familiarize yourself with the language before doing the low level stuff.
This makes Cpp and Rust harder to use then say C, so C becomes the default for me. But C is not without its issues of which we are aware. But Zig feels like it could fill that sweet spot really well, being simple enough that it's easier to pick up after a long break, but still coming with a lot of modern tooling that makes programming easier.
> You are supposed to do as much programming as you can in the high level language, and only drop into the low level language as needed.
I think that's a neat idea, but in the reverse: do as much as you can in the lower level language, and only go up to the high level language when the convenience is worth the cost.
Roc allows this: every program has a platform written in a low-level language, and then the Roc program uses the API that the platform exposes.
I think they are switching from Rust to Zig. I don’t recall exactly, but they talked about it in a video where the creator of Roc interviewed the creator of Zig.
People always laugh when I mention C# in this regard. It has so many things, like pointer, you can define that memory alignment works like in C, SIMD, AoT compilation, Span<T>. I think you can do pretty much C like programming in C#.
Or just use one language that does both high level and low level programming well, such as Rust. I use it for everything now as I haven't found anything it can't do yet, especially with its OCaml like type system.
The OG of these is probably C and Assembly. C and (Emacs) Lisp is well known. Arguably, it's the entire reason we have Python. But if you want to pair Zig against something, I would look at Lua.
Zig has so many compelling features, and I'd even be willing to give up Rust's near-perfect memory safety in some cases. But the one thing that really put me off is string handling. It's just so super tedious. I like being able to finely manage individual string memory allocations, but I really don't want to have to do it all the time. RAII is great; I wish they'd use some light (optional) RAII for strings and containers etc.
It is. I definitely agree that strings in Zig can be tedious, but the upside is that if you need it, you can build a string library that does everything you want it to do, in the way you want.
For comparison, while Rust offers a very rich string library, it's also very strict about what you can/cannot do with strings, so if your use case falls outside of that you're out of luck. With Zig, you can pretty easily roll your own and make it do what you want. (and when Zig is post 1.0, I imagine there will be some very nice pre-made string libraries by the community etc.)
Tbh I don't know enough Zig to answer that. Can you give an example? E.g. some non-performance-sensitive function that returns a string? In Rust and C++ you can treat the string exactly the same as you would an integer and it's super easy.
In C you have pain. Does the caller allocate a buffer? How does it know how big to make it? Do you have to have separate calls to calculate the required length? Etc. Can Zig work like Rust/C++ and not like C? My impression is that it can't.
An arena allocator lets you treat a series of allocations as a single block. In cases where you find yourself needing to micromanage a bunch of small memory allocations you can simply ignore freeing them individually and free the whole arena when you are done.
Yeah sorry I know what an arena allocator is. I meant how can you write code that deals with strings (joining, formatting, passing around etc.) but isn't sensitive to performance (so `std::string`-like performance is fine) in Zig without having to deal with tedious low level allocator details. If I have to pass an allocator around everywhere that's a pain in the bum.
Apparently Zig doesn't have a global allocator so it seems like you can't. (And it seems like the allocation details aren't encoded in the type anyway so it will be a disaster if you start mixing up global and non-global allocator types anyway.)
One thing I appreciate about Zig's development is that a surprising amount of effort goes into tooling and developer feedback loops rather than adding language features.
A new language can survive missing a feature for a while. It's much harder to survive if every compile, link, and dependency update feels slow. The focus on making the development cycle measured in milliseconds instead of seconds seems like a good long-term bet.
Yep. He mentioned recently in his JetBrains interview he wants Zig to be a language for the next 50 years. Rushing 1.0 for the sake of signaling to the wider industry today would be actively harmful to that goal.
Too bad it will not be adopted for anything serious in the next 50 years. There is no reasonable value proposition from a business standpoint for picking zig over rust. It is already the reality in much of the tech industry that Rust is filling the space previously occupied by C++. The fact that there now exists a safe low level language is legitimately a paradigm shift. It doesn't matter how many shiny cool things zig adds, being unsafe means it a technology stuck in the past.
There is no ETA on 1.0, but breakage has followed the pattern of it not really being hard to upgrade to a newer version, as it is very well documented on the version release notes.
writergate was not smooth, a lot of things that moved over to writer (Writer.Allocating for instance) had no documentation and I had to go read the zig source code to figure it out. the docs were just "instead of That use This"
I can tell you a bit about my own experience, as I've I used Rust for two years, but have now transitioned to Zig. I started working on a modular synthesis engine, and I realized that Javascript wouldn't give me the granular control I needed. So I decided to use Rust. This was my first foray into low level programming, so learning Rust first was great because I internalized their ownership rules. However, I started to realize that Rust was doing quite a bit of stuff behind my back, like freeing objects when dropped, or making allocations when I inserted an item. This is a big no-no for realtime programming, and Rust didn't help me understand what was happening. It was then that I read matklad's blog post on rust hard mode, https://matklad.github.io/2022/10/06/hard-mode-rust.html. I looked at some of his other blog posts too, and saw Zig. So Zig had been in the back of my mind for a while, when I decided to port a Tcl interpreter to Zig. I've been about 7 months in now making my own interpreter, and it's been a delightful language. Yes, I've had my share of memory leaks and double frees, but the debugging trace lets me capture a stack track at each reference count increment and decrement, so I'm able to hunt them down pretty well. I'm also hyper aware of memory layout and allocation failure, because allocation is fallible in Zig. The error union handling makes it pretty easy to propagate OOM though, so it's not hard to make code that's resilient to OOM. If I had tried to make this in Rust, I would have been fighting the standard library, ecosystem, and borrow checker constantly, because high level programming languages have non-trivial ownership semantics, and a lot of optimizations like epoch based variable caching just don't play nicely with references as trees.
Because it isn't memory safe. I honestly think it's beyond the point of "irresponsible" and well into "negligence" that we're still developing unsafe technologies - people are being harmed by this choice. It's one thing when you have to target specific platforms and maybe Rust wasn't an option or whatever, but the reasons to choose unsafe languages at this point are vanishingly small.
Zig is very cool, I love many aspects of it. I'll never touch it, I'll always advocate against it tbh. I'd probably advocate that software written in languages like Zig be flagged for FEDRAMP and other environments since devs seem to not care unless they're legally barred from making these sorts of choices.
I completely agree. Zig has a nicer DX no doubt - no fighting the borrow checker etc. But if you are are writing software for other people they don't care about how nice your developer experience is, they only want the software to work correctly - and how can you guarantee that the software you wrote does what you expect it to do if it's not memory safe?
It doesn’t fully guarantee that. But it guarantees you don’t have a huge class of bugs. And it makes concurrency a lot easier to reason about.
No system will likely ever guarantee that software does what you expect. That runs into the halting problem, and practically runs into a verbosity problem. But that doesn’t mean systems that give scoped guarantees aren’t amazing for building (and iterating on) reliable software.
I don't think Rust users are relevant here. It primarily comes down to personal preferences, and since Zig and Rust are so different, some will be drawn to Rust and others to Zig. If you really like a language and it suits your needs, be it Rust or any other, there's no need to look to switch. I think that the audience Zig is aimed at is low-level programmers who haven't taken a liking to Rust, which is the majority of them. Rust isn't very popular among experienced low-level programmers (certainly for a language that old), and I guess Zig is hoping to be more to their liking.
For example, I find Rust to be far too similar to C++, and it shares most of the problems I have with C++ only with much lower adoption. I'm not saying I'm ready to make the switch, but at least Zig offers a different approach that's intriguing to me.
Try it if you want full control over every memory and IO operation and "drop". If you hit "goto-definition", you eventually get to see the OS switch statements and syscalls. There's not much magic.
Do not try it if you are scared of memory management and memory leaks.
Not working within the bounds of lifetimes, and more ecosystem that doesn't live in the world of lifetimes, gives Zig some of the wonderful dev ergonomics of Rust while making it easier to prototype.
For small, short game dev, or even smaller embedded projects, this ends up being a wonderful way to live as often times you're trying to eke out performance in ways that would require breaking out of whatever type abstractions or using unsafe.
For long-lived systems, for systems that need to have lots of people with various skill levels work on them, for a mature ecosystem, for a language/standard library with stability... You probably don't want to pick Zig right now. Some of these points will change over time with Zig becoming more mature, some won't. Zig will always be super cool to build things in.
As far as most low-level programmers not liking Rust like some other commenters say, lol, lmao even.
I believe if the work you do fits well with Node & TS, you have no reason to use zig (other than for learning or curiosity). Like, if you don't need every drop of performance, memory layout & control, there's more downsides to using zig. Idk, cruds or "enterprise" stuff or websites don't benefit from zig.
You can't run Node on embedded systems, because there isn't enough memory.
A compiled zig program can be only several kilobytes with no depedenencies.
Array access programmed in low-level languages can be optimized with SIMD and parallelization, which will be orders of magnitude faster than the same thing on Javascript. Text processing, image manipulation, video processing, hashing, etc.
There is like infinite reasons to not use Javascript.
There are different licenses that used to be referred to as "MIT", and explicitly stating "Expat" tells you which one they're referring to (in this case, the "standard" one). This is largely unnecessary, as nearly all mentions of the MIT license refer to this one.
Carpentry would have been ideal career. Building real things, artisanal or not, and nobody insisting yet that the chest of drawers you're building include AI
I think bun is moving to rust because Anthropic owns it and the devs there like rust. So why would they invest in another implementation? Sad to see a good zig example go, but as soon as Anthropic bought it I wrote the project off.
I am not aure either, but bun wasn't using normal zig and there was drama about upstreaming. Combine that with anthropics desire to show they can help rewrite everything in rust and that probably accounts for some of it.
Bun has de-facto refused to use incremental compilation in Zig for ages. It got to the point where Jarred somehow seems to have forgotten that the feature exists.
In any case Bun has already committed to the Rust slop switch, so it doesn't matter anymore.
bun seems to be committed to slop rust already. so, with their ethic, maybe we should just disassociate them from zig and let them go realize their slop dreams?
zig is on its way to improving compilation times in its own pace and does so for the benefit of the project and everyone involved, so what is left to care for about bun by anthropic’s past?
I bet they'll ultimately reverse course on this, or the there will be a bun / zig fork becomes the de facto bun. Despite what the influencers say, I'm convinced you cannot vibe code a conversion this big. It will need a ton of human intervention. And for brand narrative reasons, Anthropic won't commit to such a path.
It depends on how thorough the test infrastructure is I think. Something like curl with its immaculate tests could probably get autonomously ported if you threw infinite tokens at it because you have deterministically defined what finished looks like. But I think you are likely right in this case.
> bun seems to be committed to slop rust already. so, with their ethic, maybe we should just disassociate them from zig and let them go realize their slop dreams?
Closing your eyes and pretending a problem does not exist is the a good solution. The fact of the matter is one of the biggest projects that used Zig thought that the devX was so bad that they opted to rewrite their entire 1M LOC project into a different language. This is a nightmare scenario for most companies, and will motivate similar sized companies/project to pick another language that will not require this than to risk using Zig. Also, Zig’s flippant attitude about Bun’s request (among other viewpoints) only further adds to why bigger projects would want to stay away from Zig.
> The fact of the matter is one of the biggest projects that used Zig thought that the devX was so bad that they opted to rewrite their entire 1M LOC project into a different language.
That is not the fact of matter. The fact is it got bought by Anthropic. And in larger scheme of things Bun is one example Claude capabilities of translation. And even if doesn't work, it just a part of Claude desktop stack so still have millions of installs.
Not really, if it doesn’t work then it will hurt their flagship desktop app’s stability, which would negatively affect their placing in an already cutthroat AI arms. Claude Code is the few business asset that Claude has the least moat compared to other providers( even open source). They can’t afford to be sloppy and use a buggy JS engine.
Huh, I don't know which world you live in. In my world trillion dollars companies regularly release absolute crap of the software that hardly affects their industry usage or popularity.
In case anyone is not following the AI tool's biggest growth is in enterprises. And usability, quality or stability is measured very differently there compared to individual users. In my last 20 years of experience of using enterprise software and tools being third rate is never any issue.
I don't remember where it was said first, but I think the problem was not "AI drama" or that zig doesn't have "a good solution". It was more a mismatch between Bun's & Zig's goals. Bun wants to move fast & break things, even more now after getting acquired, but Zig punishes that. Zig requires you to handle everything carefully, there's no GC or big runtime to let you "break things", zig will let you just segfault.
Companies like TigerBeetle can and will benefit from zig's model.
But this goes to my second point; it seems like Zig wasn’t open to any compromise about the solution that Bun submitted and one they built in house. Is it the Zig culture to reject a pull request like that wholesale? It’s really odd for them to have such a flippant attitude and not to even try offer ways that they could use the pull request or things that they need to tweak to make it more inline with what they want. Companies want to use languages that have a understanding committee, and are willing to work together to create solutions, not just say “No, this isn’t what we want, and we are building a similar system anyway so don’t even bother to try again”. It just looks unprofessional.
A large, complex, unasked for PR is pretty likely pointless to throw at any serious project. (Well, it's pointless if your goal is to merge something.)
Working together is a two-way process. To land a big change, the bun people probably needed to have been working/coordinating with the zig people throughout. E.g., zig outright cannot accept PRs that break the language in unplanned ways and any conflicts with the roadmap would need to be resolved.
I would assume the bun people know all this. That makes it more of a publicity stunt than a serious attempt to contribute to zig, and we should probably all treat it that way.
Of course, and it is expected that large pull requests/RFCs are iterated on. I will not believe Bun seriously asked for a pull request to be merged with absolutely no expectation of back and forth discussion. But this isn’t what happened. The whole reason everyone thought it was rejected by Zig because Bun used LLM to generate it was because they responded in a way that someone would if they didn’t want a certain pull request accepted under any circumstances. Which is my point; it I just insane that their largest project submitted a pull request, and they just rejected it with prejudice, gave some statement saying the real and potentially fixable reasons why, then turn around and say we don’t want your help, we are doing this in house.
My interpretation of what they said is closer to “we already improved compilation speeds by 4x and we did it without compromising our plans to go much further - also this PR introduces specific timing bugs.”
Compromising on project goals just because someone with somewhat different goals made a pull request doesn’t exactly scream responsible and professional to me. The way I understand it, many people appreciate Zig because it’s very consistent and restrained about the kinds of problems it’s trying to solve and how, so being very careful with accepting complex, externally developed solutions seems perfectly in character for the language.
I’m not sure how well the Zig developers have handled their communication, so perhaps there really was room for improvement there.
"Therefore, to implement this feature without an avalanche of bugs and inconsistencies, we need to make language changes."
"Put more simply, we are going to make these enhancements, but hacking them in for a flashy headline isn’t a good outcome for our users. Instead we’re approaching the problem with the care it deserves, so that when we ultimately ship it, we don’t cause regressions."
"So instead of wasting time writing a more robust implementation of this LLVM module splitting logic for a relatively minor improvement, we have instead put that effort towards features like self-hosted backends and incremental compilation, which can improve compilation speed by orders of magnitude.
[...]
There’s the 4x speedup claimed by the Bun team, already available on Zig 0.16.0!"
> The fact of the matter is one of the biggest projects that used Zig thought that the devX was so bad
It's unlikely to be just a devex issue. The fact of the matter is that a memory unsafe language is an extremely tough sell today, and companies that have a security team at all have likely already made or are planning on making policies like https://chromium.googlesource.com/chromium/src/+/master/docs...
There's a reason "rewrite it in Rust" was a meme long before LLM coding tools or this Bun drama. With AI accelerated fuzzing techniques and similar, memory safety is rapidly going to become a basic requirement of anything run in a production environment.
If you haven't tried Zig since 0.16.0 was released, I highly recommend having a look. The release notes for this release were huge!!
https://ziglang.org/download/0.16.0/release-notes.html
reply