Module sys/future
IoFuture — the opaque handle every async I/O extern hands back.
This is the one type the whole std/sys async surface is written in terms
of: sys/file, sys/dir, sys/tcp, sys/udp, sys/unix, sys/dns,
sys/process and sys/timer all return it, and io.await is what turns
it back into an i32. Every public module above them (std/fs,
std/net, std/process, std/time/sleep) reaches it only through those.
Stability
unstable — this is an ABI, not an API. __yo_io_future_t is emitted by
the compiler's own runtime (src/codegen/async/) with a layout the await
codegen indexes directly (state, result), so its shape is pinned to
the async state-machine representation and moves with it, not with a
library decision. Its value convention is one thing on every platform
since 2026-10-03: a resolved i32 that is a non-negative syscall result
or a negative errno, with sys/dns alone resolving to its documented
DNS_ERR_* codes (above every errno). The raw i32 stays — a typed
Result(i32, IoError) channel at the extern boundary was considered and
declined (plans/ASYNC_IO_API_AUDIT.md Q2): IoError.check and
IoError.from_result turn the answer into a value at the call site. What
would still have to be true to freeze it: a state-machine layout that
codegen promises to keep.
Types
A pending (or already finished) I/O operation, resolved with io.await.
Impl(Concrete(__yo_io_future_t), Future(i32)): an opaque C struct laid
out like an async state machine, so await reads its state and result
the same way it reads a Yo async block's. state 0 means still in
flight, -1 already completed (the common case on macOS and Windows,
where most file operations run synchronously and are handed back finished)
and -2 aborted — see the effects model in AGENTS.md.
It resolves to a raw i32: a non-negative syscall result (an fd, a byte
count, or 0) or a NEGATIVE errno, on every platform (the Windows runtime
maps Winsock's codes to errno). Nothing here throws, so error handling is
IoError.from_result / IoError.check at the call site. sys/dns
resolves to its own DNS_ERR_* codes, which std/net/dns maps to
DnsError. io.state reads an in-flight raw future as Running: it was
submitted when it was created, so it is never Cold.