Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Zig's async implementation is the best of any languages, because you can run them without an event loop and in that case they will be simply synchronous. This completely eliminates the function coloring problem.


It absolutely doesn't, because it changes the semantics of what's being called, introducing deadlocks.


Zig async doesn't change semantics of how things are called. If something is called sequentially, it will execute sequentially with respect to itself, with or without async.


It doesn't change the semantics, the documentation says: https://ziglang.org/documentation/0.10.1/#Async-Functions

"Async functions can be called the same as normal functions." Only when you have an event loop, the order async functions are running might be different because of the scheduling, but everything else is the same.


Except Modula-2 and Concurrent Pascal, among several others did it first, in the 1970's.


Usually the coloring problem IME goes the other way: you want to run a synchronous/blocking function in an async context. How does Zig deal with that?


Function calls?


And now you're blocking the event loop.


If you chose to use cooperative concurrency, it's because you can make it cooperate. Use preemption otherwise.


They have await keyword. This is noop in sync mode


Did you mean the `nosuspend` keyword? It only asserts nothing suspends rather than there being a "sync" mode.


Does that mean the return types are the same when you use async or not on the same function?


No, async functions still return an async Frame (the semantic equivalent of a Rust Future) which evaluates to the result, rather than the result itself. Zig's "colorless async" description comes from the implicit infectiousness by default rather than colors not "being there":

A function which contains a suspend point becomes implicitly async. An await is implicitly a suspend until the Frame completes, so it also makes the caller's function async. Calling a async function without the `async` keyword is implicitly `await (async f(args))` so same deal.

You ended up not needing to know if `f()` is async or not when you call it ("effectively colorless"). If it is, it just makes you async and bubbles up until the sync boundary (an `async f()` call, `main()`, or an `export fn` C API).

Zig's stdlib could handle the main() case and spin up an event loop if you requested via `io_mode = .evented`. Some stdlib stuff like net/fs would then use the event loop while others like os/Thread would stay the same if you still wanted to do sync stuff.

This was the common case, but async is a lang construct not an stdlib construct so it works for all targets. For example, you could use it in wasm, the kernel, etc. You would just need to write your own starting / scheduling of the frames, known generically as an "event loop".


No, there still are sync and async versions of functions. Zig just implicitly chooses which version to run and implicitly inserts await points. There's still a runtime and event loop if you use async. It's an async like in any language, but it relies on compiler magic instead of having locally explicit syntax.


One can check this via static analysis as the complete code tree can be inferred with relative ease (only package paths and usingnamespace are depending on comptime) + third party code will be able to query compiler info, so I do not expect this to be a big problem once static analysis symbols can be enforced as unique (see https://github.com/ziglang/zig/issues/14656#issuecomment-143... and follow-up for the use cases of static analysis tooling). Also, since there is currently no problem: YAGNI and solve it once a use case comes up.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

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

Search: