# Zig's New Async I/O

**URL:** <https://ziggit.dev/t/zigs-new-async-i-o/10947>\
**Category:** News\
**Tags:** language, standard-library\
**Created:** [July 12, 2025, 11:05pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947 "2025-07-12T23:05:39Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [July 12, 2025, 11:05pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/1 "2025-07-12T23:05:40Z")

</div>

> **[Zig's New Async I/O](https://kristoff.it/blog/zig-new-async-io/)**
>
> Asynchronicity is not concurrency.

---

<div class="post-metadata">

**Author:** ![smlavine](https://ziggit.dev/letter_avatar_proxy/v4/letter/s/eb9ed0/32.png) [@smlavine](https://ziggit.dev/u/smlavine)\
**Post date:** [July 13, 2025, 12:21am UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/2 "2025-07-13T00:21:06Z")

</div>

I’m excited about these changes. I think they’ll be a big improvement over the current API, even for regular blocking IO operations.

Question – will the convention for the `IO` parameter extend to all parts of the standard library (i.e. even `std.debug`)?

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [July 13, 2025, 2:06am UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/3 "2025-07-13T02:06:35Z")

</div>

I think `std.debug.print` for example will continue to have its same definition:

```zig
/// Print to stderr, silently returning on failure. Intended for use in "printf
/// debugging". Use `std.log` functions for proper logging.
///
/// Uses a 64-byte buffer for formatted printing which is flushed before this
/// function returns.
pub fn print(comptime fmt: []const u8, args: anytype) void {
    var buffer: [64]u8 = undefined;
    const bw = lockStderrWriter(&buffer);
    defer unlockStderrWriter();
    nosuspend bw.print(fmt, args) catch return;
}

```

As for loading debug info, that might fall under the same umbrella, or it might be better to make it more reusable.

Another question is the default log function. Probably it will continue to have the same behavior, writing log messages to stderr, independent from any Io implementation, relying on the user to override that behavior if desired.

The important thing is that third party, reusable packages in the ecosystem will use this pattern and therefore be useful with any I/O strategy.

---

<div class="post-metadata">

**Author:** ![KilianHanich](https://ziggit.dev/user_avatar/ziggit.dev/kilianhanich/32/8254_2.png) [@KilianHanich](https://ziggit.dev/u/KilianHanich)\
**Post date:** [July 13, 2025, 2:14am UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/4 "2025-07-13T02:14:34Z")

</div>

I actually have a question: What actually is the purpose of `nosuspend` now?

Until now it was as far as I understand things for handling certain cases about async functions, but since they don’t exist anymore (on a language level), what does it do?

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [July 13, 2025, 2:21am UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/5 "2025-07-13T02:21:25Z")

</div>

[Before](https://ziglang.org/documentation/0.11.0/#Keyword-Reference):

> The `nosuspend` keyword can be used in front of a block, statement or expression, to mark a scope where no suspension points are reached. In particular, inside a `nosuspend` scope:
> 
> - Using the `suspend` keyword results in a compile error.
> - Using `await` on a function frame which hasn’t completed yet results in safety-checked [Undefined Behavior](https://ziglang.org/documentation/0.11.0/#Undefined-Behavior).
> - Calling an async function may result in safety-checked [Undefined Behavior](https://ziglang.org/documentation/0.11.0/#Undefined-Behavior), because it’s equivalent to `await async some_async_fn()`, which contains an `await`.
> 
> Code inside a `nosuspend` scope does not cause the enclosing function to become an [async function](https://ziglang.org/documentation/0.11.0/#Async-Functions).

Now: nothing

In the future, it might be reused for the equivalent purpose as before: assert that no suspension points will occur within the block.

---

<div class="post-metadata">

**Author:** ![mlugg](https://ziggit.dev/user_avatar/ziggit.dev/mlugg/32/437_2.png) [@mlugg](https://ziggit.dev/u/mlugg)\
**Post date:** [July 13, 2025, 12:03pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/6 "2025-07-13T12:03:49Z")

</div>

Just to clarify: “suspension points” is referring specifically to stackless coroutines, which require language-level support (see [the corresponding proposal](https://github.com/ziglang/zig/issues/23446)). `nosuspend` doesn’t have any special interaction with the `std.Io` interface; if it stays in the language it will specifically be related to that proposal.

---

<div class="post-metadata">

**Author:** ![huntrss](https://ziggit.dev/user_avatar/ziggit.dev/huntrss/32/6342_2.png) [@huntrss](https://ziggit.dev/u/huntrss)\
**Post date:** [July 13, 2025, 1:01pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/7 "2025-07-13T13:01:40Z")

</div>

Hi,  
with this Io abstraction, it should be possible to implement some kind of capability based Io (e.g. file system access), something like [cap-std](https://crates.io/crates/cap-std). Is this something that is envisioned or planned for the Zig Standard library or is it something currently “left” for the community in the form of a 3rd party library?

Super interesting work, btw… I need to dig deeper into it, especially since I consider myself as a self-proclaimed async Rust expert 😉

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [July 13, 2025, 2:48pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/8 "2025-07-13T14:48:09Z")

</div>

Going to be fun to implement something like task prioritization under cooperative multitasking on microcontrollers. Sounds like this io interface will make that relatively easy. Really looking forward to improved code reusabilty!!!

---

<div class="post-metadata">

**Author:** ![huntrss](https://ziggit.dev/user_avatar/ziggit.dev/huntrss/32/6342_2.png) [@huntrss](https://ziggit.dev/u/huntrss)\
**Post date:** [July 13, 2025, 8:40pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/9 "2025-07-13T20:40:56Z")

</div>

Another question: I could not find the Io interface on Github. Can someone provide a link to it?

---

<div class="post-metadata">

**Author:** ![kristoff](https://ziggit.dev/user_avatar/ziggit.dev/kristoff/32/9_2.png) [@kristoff](https://ziggit.dev/u/kristoff)\
**Post date:** [July 13, 2025, 8:45pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/10 "2025-07-13T20:45:43Z")

</div>

The async-await-demo branch has, well, a demo of the new async await stuff, and this gist shows a usage example: [async await demo · GitHub](https://gist.github.com/andrewrk/1ad9d705ce6046fca76b4cb1220b3c53)

None of this is ready for prime time, but you can take it for a spin. The threadpool implementation works on all platforms, the event loop one (green threads) only works on x86\_64 linux for now.

---

<div class="post-metadata">

**Author:** ![huntrss](https://ziggit.dev/user_avatar/ziggit.dev/huntrss/32/6342_2.png) [@huntrss](https://ziggit.dev/u/huntrss)\
**Post date:** [July 14, 2025, 9:12am UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/11 "2025-07-14T09:12:32Z")

</div>

Here is the link to the Io vTable in the demo branch: [Io vTable](https://github.com/ziglang/zig/blob/async-await-demo/lib/std/Io.zig#L891).  
For everyone who wants to dig deeper.

---

<div class="post-metadata">

**Author:** ![cryptocode](https://ziggit.dev/user_avatar/ziggit.dev/cryptocode/32/817_2.png) [@cryptocode](https://ziggit.dev/u/cryptocode)\
**Post date:** [July 14, 2025, 11:31am UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/12 "2025-07-14T11:31:59Z")

</div>

> [@kristoff](#):
>
> usage example: [async await demo · GitHub](https://gist.github.com/andrewrk/1ad9d705ce6046fca76b4cb1220b3c53)

Very excited about this new API and everything it enables.

After reading your blog post in combination with that demo gist, I do have  
one concern: getting cleanup right in the face of refactoring, both when moving code around and when introducing errors to functions.

If I understand correctly, the rule is that any `async` operation happening _before_ a `try await` must have a pending `defer cancel` (or an await), otherwise we have a bug.

Isn’t it relatively easy to go wrong if you introduce errors to functions that previously were infallible?

Example: if `calcSum` down the line is changed to also return an error and you recompile, you’ll get a compile error because you don’t have `try` in front of `first_half.await` and `second_half.await`

Now, as one is used to in this situation, you add the `try` to call sites and  
everything seems to work in the happy path. But what if `calcSum` fails at runtime?

As there’s a missing `defer cancel` on second\_half, we have bug. You won’t forget to add the `try` because the compiler tells you too, but I fear it’s too easy to miss or forget spots where cancelation is also needed when errors are introduced.

If this is correct, we might need tooling to help avoid these issues. Always doing defer-cancel just in case doesn’t seem right.

Maybe it’s possible to make the equivalent of arenas for async so you only need one defer in functions that uses a non-trivial amount of async. Something like `io.scopedIo()` paired with a single defer which takes care of any cleanup? If a “DebugIo” is planned to catch these and good coverage is possible, that could be a fine solution too.

---

<div class="post-metadata">

**Author:** ![KilianHanich](https://ziggit.dev/user_avatar/ziggit.dev/kilianhanich/32/8254_2.png) [@KilianHanich](https://ziggit.dev/u/KilianHanich)\
**Post date:** [July 14, 2025, 12:19pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/13 "2025-07-14T12:19:14Z")

</div>

This is already something you need to keep in mind for anything in Zig really, including general memory allocations, not just async io or similar.

---

<div class="post-metadata">

**Author:** ![cryptocode](https://ziggit.dev/user_avatar/ziggit.dev/cryptocode/32/817_2.png) [@cryptocode](https://ziggit.dev/u/cryptocode)\
**Post date:** [July 14, 2025, 12:31pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/14 "2025-07-14T12:31:27Z")

</div>

For memory allocations we have good idioms and tools to help prevent and find bugs (DebugAllocator and valgrind to find bugs, ArenaAllocator to, among other things, help reduce cognitive load, etc). That’s why I brought it up really - to ask if we’ll have similar tooling and idioms for async io.

I think this is a bit different, because any function can be used by both regular code and async code. Once you introduce failures, you have to check for async callsites and see if introducing a new early exit (due to try) also means adding cleanup elsewhere.

---

<div class="post-metadata">

**Author:** ![kristoff](https://ziggit.dev/user_avatar/ziggit.dev/kristoff/32/9_2.png) [@kristoff](https://ziggit.dev/u/kristoff)\
**Post date:** [July 14, 2025, 12:48pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/15 "2025-07-14T12:48:47Z")

</div>

You can make a DebugIo that gives you stack traces when you deinit it and it sees that some Future was not awaited or canceled.

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [July 14, 2025, 12:48pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/16 "2025-07-14T12:48:50Z")

</div>

the ability to have `DebugIo` is one of the benefits of Io as an interface that Andrew gave, so it will probably happen

---

<div class="post-metadata">

**Author:** ![cryptocode](https://ziggit.dev/user_avatar/ziggit.dev/cryptocode/32/817_2.png) [@cryptocode](https://ziggit.dev/u/cryptocode)\
**Post date:** [July 14, 2025, 12:51pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/17 "2025-07-14T12:51:42Z")

</div>

Sounds good, hope to see that in std

---

<div class="post-metadata">

**Author:** ![fearedbliss](https://ziggit.dev/user_avatar/ziggit.dev/fearedbliss/32/8875_2.png) [@fearedbliss](https://ziggit.dev/u/fearedbliss)\
**Post date:** [July 17, 2025, 2:54am UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/18 "2025-07-17T02:54:01Z")

</div>

Looking forward to messing around with this on FreeBSD. I’m happy that the ZSF and folks are not rushing to just jam in whatever async strategy into the language to keep up with hype or pressure. Just keep on doing exactly what you guys are doing. I’m having a lot of fun writing my personal work in Zig and releasing a lot of them as well, and looking forward to maintaining my robust, optimal, and reusable apps 🙂.

---

<div class="post-metadata">

**Author:** ![ppipelin](https://ziggit.dev/user_avatar/ziggit.dev/ppipelin/32/4631_2.png) [@ppipelin](https://ziggit.dev/u/ppipelin)\
**Post date:** [July 18, 2025, 2:50pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/19 "2025-07-18T14:50:51Z")

</div>

In function signature, should we put (allocator, io) or (io, allocator) ?

---

<div class="post-metadata">

**Author:** ![kristoff](https://ziggit.dev/user_avatar/ziggit.dev/kristoff/32/9_2.png) [@kristoff](https://ziggit.dev/u/kristoff)\
**Post date:** [July 18, 2025, 5:14pm UTC](https://ziggit.dev/t/zigs-new-async-i-o/10947/20 "2025-07-18T17:14:35Z")

</div>

damn, hard question. IMO `io` should go first

[Next page](https://ziggit.dev/t/zigs-new-async-i-o/10947.md?page=2)
