Module io/index

io/index
Stability: unstable — the traits' core signature is the open question. `read` and `write` take a caller buffer as `*(u8)` + length because Yo has no slice type; if one arrives, that parameter shape is what it would replace, and that is a breaking change to every implementor (`File`, `TcpStream`, `UnixStream`, `TlsStream`, the stdio handles, `BufReader`/`BufWriter`, `Child`'s pipe handles) at once. It is also why safe code cannot implement these traits at all today, only consume them through the pointer-free defaults. Two smaller items ride along: `read_exact` is a `BufReader` method rather than the `Reader` default Rust has, and `lines()` waits on the non-throwing read named above. `Seek(From)` is newer still — 2026-09-09 — and inside its one-release window. `copy`, and the `read_to_end`/`read_to_string`/`write_all` defaults, are expected to keep their names and meanings. — stable modules only change additively; this one may still change.

Async I/O traits — the D5 redesign (plans/archive/STD_API_AUDIT.md §D5).

Reader and Writer are the async byte-stream interfaces every I/O source/sink implements: File (std/fs), TcpStream (std/net), and the Stdin/Stdout/Stderr handles in std/io/stdio. All methods run on the single-threaded async event loop and report failure by throwing IoExn (D1 style 1 — everything on the io path throws).

The raw read/write take a caller buffer as *(u8) + length — the same shape TcpStream.read and the sys layer already speak. Raw pointers are not available in safe code, so IMPLEMENTING or calling the raw methods directly needs pragma(Pragma.AllowUnsafe);; safe code uses the pointer-free defaults (read_to_end, read_to_string) and the per-type conveniences on each concrete type.

The generic BufReader(R) / BufWriter(W) wrappers landed in ./bufio.yo, and the std/sys/bufiostd/io move COMPLETED 2026-08-28 (fd-based pair deleted; the compiler's three stdin consumers read through BufReader(Stdin)).

The Dyn(Reader) spelling works too (C17 fixed 2026-08-29; pinned in tests/io/async_traits.test.yo): a Dyn(Reader) dispatches read and the defaults through its vtable and can be the R of BufReader(R). Concrete types carry NO duplicate spellings of the trait operations — File/TcpStream read-everything is read_to_end/read_to_string, the trait defaults (D2: one Rust-shaped name per operation); their write_string/write_bytes conveniences stay because the trait has no counterpart.

Seek(From) joins them as of 2026-09-09, and is the one trait here that is SYNCHRONOUS — moving a position is arithmetic on a handle's own state, not I/O. Implemented by File; see the trait's own doc for why it takes the reference-point type as a parameter and has no rewind.

STILL TO COME: a buffered lines(). The async iterator protocol it was waiting on landed 2026-09-11 (std/async/stream.yo), so the remaining blocker is this trait's own error style: a Stream reports failure in its ITEM and read/read_line THROW, which cannot be caught inside an async body. See plans/backlog/ASYNC_LINES_NEEDS_A_NONTHROWING_READ.md for the try_read proposal.

Stability

unstable — the traits' core signature is the open question. read and write take a caller buffer as *(u8) + length because Yo has no slice type; if one arrives, that parameter shape is what it would replace, and that is a breaking change to every implementor (File, TcpStream, UnixStream, TlsStream, the stdio handles, BufReader/BufWriter, Child's pipe handles) at once. It is also why safe code cannot implement these traits at all today, only consume them through the pointer-free defaults. Two smaller items ride along: read_exact is a BufReader method rather than the Reader default Rust has, and lines() waits on the non-throwing read named above. Seek(From) is newer still — 2026-09-09 — and inside its one-release window. copy, and the read_to_end/read_to_string/write_all defaults, are expected to keep their names and meanings.

Traits / Modules

Seek trait-function
fn(From : Type) -> Type(1)

Something whose read/write position can be moved — Rust's io::Seek.

SYNCHRONOUS, unlike Reader/Writer: moving a position is arithmetic on a handle's own state, not I/O. File.seek resolves the target itself and never touches the descriptor (its reads and writes are positional, so the descriptor sits at 0 forever), so there is nothing to await. Keeping it synchronous also means a Seek bound composes with a plain function.

SeekFrom lives in std/fs/types because that is where the File API it was written for lives; the trait takes it as a parameter rather than re-declaring it, so there is one spelling of "from where".

There is deliberately no rewind (Rust has one): every absolute move needs a From VALUE naming the beginning, and a trait generic over From cannot name one. A default rewind(self, from, exn) would make the caller pass the very thing rewind exists to hide, so the convenience lives on the concrete type instead — see File.rewind.

Type Parameters

NameTypeNotes
FromTypecomptime
Reader trait
Reader

An async source of bytes.

Methods

read : fn(self : Self, buf : *(u8), size : usize, io : Io) -> Impl : (Future[Future](usize) IoExn : IoExn)

Read up to size bytes into buf. Resolves to the number of bytes actually read; 0 means end-of-stream. Throws IoExn on failure.

Parameters

NameTypeNotes
selfSelf
buf*(u8)
sizeusize
ioIo

Returns: Impl : (Future[Future](usize) IoExn : IoExn)

read_to_end : fn(self : Self, io : Io) -> Impl : (Future[Future](ArrayList(u8)) IoExn : IoExn)

Read until end-of-stream, resolving to all remaining bytes. Loops the raw read with an internal chunk buffer, so implementors get it for free and callers never touch a raw pointer.

Parameters

NameTypeNotes
selfSelf
ioIo

Returns: Impl : (Future[Future](ArrayList(u8)) IoExn : IoExn)

read_to_string : fn(self : Self, io : Io) -> Impl : (Future[Future](String) IoExn : IoExn)

Read until end-of-stream and decode as UTF-8. Unlike the UNCHECKED per-type conveniences that predate D5, this validates: malformed bytes throw IoError.InvalidData (use read_to_end + String.from_utf8 to see the exact offset).

Parameters

NameTypeNotes
selfSelf
ioIo

Returns: Impl : (Future[Future](String) IoExn : IoExn)

Implementors

Writer trait
Writer

An async sink of bytes.

Methods

write : fn(self : Self, buf : *(u8), size : usize, io : Io) -> Impl : (Future[Future](usize) IoExn : IoExn)

Write up to size bytes from buf. Resolves to the number of bytes actually written (which may be less than size). Throws IoExn on failure.

Parameters

NameTypeNotes
selfSelf
buf*(u8)
sizeusize
ioIo

Returns: Impl : (Future[Future](usize) IoExn : IoExn)

flush : fn(self : Self, io : Io) -> Impl : (Future[Future](unit) IoExn : IoExn)

Flush any buffered bytes through to the underlying sink. Unbuffered writers resolve immediately.

Parameters

NameTypeNotes
selfSelf
ioIo

Returns: Impl : (Future[Future](unit) IoExn : IoExn)

write_all : fn(self : Self, buf : *(u8), size : usize, io : Io) -> Impl : (Future[Future](unit) IoExn : IoExn)

Write ALL size bytes from buf, looping the raw write over short counts. Throws IoError.WriteZero if the sink stops accepting bytes before everything is delivered.

Parameters

NameTypeNotes
selfSelf
buf*(u8)
sizeusize
ioIo

Returns: Impl : (Future[Future](unit) IoExn : IoExn)

Implementors

Functions

copy function
fn(generic(R : Type, W : Type), r : R, w : W, io : Io, where(R <: Reader, W <: Writer)) -> Impl(Future(u64, IoExn))

Copy everything from r to w until r reaches end-of-stream, resolving to the total number of bytes copied (u64 so totals larger than a 32-bit usize cannot overflow). Does NOT flush w.

Type Parameters

NameTypeNotes
RTypecomptime
WTypecomptime

Parameters

NameTypeNotes
rR
wW
ioIo

Returns: Impl(Future(u64, IoExn))