Zig's new plan for asynchronous programs

Async keywords and “function coloring”

  • Several commenters dislike explicit async keywords that “infect” call graphs; others find them useful as visible markers of potential I/O pauses.
  • Some argue that IO shouldn’t be uniquely marked; panics, allocation, stack usage etc. are equally “effects” and deserve a principled effect system rather than ad‑hoc async.
  • There is debate over whether Zig’s approach actually removes coloring or just changes it from “async vs sync” to “does IO vs not”.

Zig’s Io model

  • Zig introduces an explicit Io parameter for any function that may perform I/O; the same API works with:
    • blocking threaded runtimes (Io.Threaded),
    • single-threaded variants,
    • and planned evented / stackless coroutine runtimes.
  • io.async(f, args) creates a future; future.await(io) waits; io.concurrent guarantees parallelism (or errors).
  • The same function is used for both sync and async I/O; the runtime choice lives in the Io instance, not in function signatures or keywords.

Comparisons to other languages

  • Rust: async is seen as a half-effect-system bolted onto the type system (Future), causing ecosystem splits between sync and async APIs and runtime lock-in (e.g. Tokio).
  • Go: goroutines + channels seen as “green threads + queues”; some say Zig’s Io.Queue + Io.select can replicate Go’s select, others stress that Go channels’ rendezvous semantics and synchronization guarantees are heavier than simple futures or queues.
  • Haskell: many note the similarity to IO/Reader monads and explicit effect tokens, though Zig doesn’t treat them as monads in the language.
  • JS, Python, C#: discussed as examples of “viral” async/await; Go and Java virtual threads as examples of colorless or “everything async” models.

Ergonomics, DI, and explicit effects

  • Supporters like explicit Allocator and Io as “sweet smells” in a systems language: no hidden runtimes, easy to swap implementations, good for embedded and OS work.
  • Critics worry about “prop drilling” (passing Io/Allocator through long call chains), and suggest context objects or DI frameworks; others strongly oppose such magic and prefer explicit parameters.
  • Some point out the ecosystem effect: if all std I/O uses Io, libraries naturally become runtime-agnostic, unlike typical Rust/Python async splits.

Concurrency, safety, and structure

  • Thread safety remains a concern: libraries must still reason about threaded vs single-threaded Io and document costs.
  • Structured concurrency patterns are possible via Io.Group and defer future.cancel(io), but correctness still relies on programmer discipline.
  • Evented/stackless coroutine support and suspend/resume primitives are still under design; complex server and FFI scenarios are seen as important tests of the model.