Module io/bufio

io/bufio
Stability: unstable — one thing here is expected to move and one thing is waiting on the language. `read_exact` is a `BufReader` METHOD returning `Option(ArrayList(u8))`, where Rust's is a `Read` trait method: the Rust-shaped end state is a `Reader` default (so a `File` or `TcpStream` gets it unbuffered too), which would leave two spellings of one operation unless this one is removed — a breaking change, so it waits for a window. And `lines()` cannot exist until Yo has an async iterator protocol, which is why `read_line` is the primitive; adding `lines()` later is additive, but it may well change what shape `read_line` wants to be. The no-flush-on-drop rule for `BufWriter` is NOT an open question — it is a deliberate match for tokio's `BufWriter` (a `Dispose` cannot await), and it is documented above because callers must act on it. — stable modules only change additively; this one may still change.

Buffered wrappers over ANY async Reader/Writer — the D5 generic BufReader(R) / BufWriter(W) (plans/archive/STD_API_AUDIT.md §D5), the replacement for the fd-only std/sys/bufio pair it has now replaced.

BufReader(R) reduces the number of inner reads by filling an internal buffer in capacity-sized chunks; BufWriter(W) batches small writes and delivers them with write_all when the buffer fills or flush is called. Both implement the trait they wrap, so a BufReader(R) is itself a Reader (and gets read_to_end/read_to_string for free) and a BufWriter(W) is a Writer.

BufWriter(W) does NOT flush on drop. A Dispose cannot await the inner writer's async write, so — like tokio's BufWriter, and unlike the deleted fd-based std/sys/bufio writer, whose dispose could issue a sync positional syscall — buffered bytes still in the writer when it is dropped are LOST. Call flush(io) before letting it go.

lines() is still absent, but no longer for the reason this doc used to give. The async iterator protocol EXISTS now (std/async/stream.yo); what blocks lines() is that a Stream reports failure IN its item while read_line THROWS, and a ctl handler can neither be installed across a suspension nor stored in a ref struct. So the line primitive is still read_line (.None on clean EOF), and the fix is a Reader that can return its failure — plans/backlog/ASYNC_LINES_NEEDS_A_NONTHROWING_READ.md.

The fd-based std/sys/bufio pair is GONE (2026-08-28): the compiler's own three consumers — lsp/server, lsp/transport, check_watch — read stdin through BufReader(Stdin) now, and the module and its test are deleted. That migration waited on a seed carrying #299's shadow-registration fix, because yo build compiles src/ with the SEED and the older one mis-emitted this module's match bindings; v0.2.18 carries it.

Stability

unstable — one thing here is expected to move and one thing is waiting on the language. read_exact is a BufReader METHOD returning Option(ArrayList(u8)), where Rust's is a Read trait method: the Rust-shaped end state is a Reader default (so a File or TcpStream gets it unbuffered too), which would leave two spellings of one operation unless this one is removed — a breaking change, so it waits for a window. And lines() cannot exist until Yo has an async iterator protocol, which is why read_line is the primitive; adding lines() later is additive, but it may well change what shape read_line wants to be. The no-flush-on-drop rule for BufWriter is NOT an open question — it is a deliberate match for tokio's BufWriter (a Dispose cannot await), and it is documented above because callers must act on it.

Types

BufReader type-function
fn(R : Type) -> Type

Buffered reader over any Reader.

Type Parameters

NameTypeNotes
RTypecomptime

Trait Implementations

impl(generic(R : Type), where(R <: Reader), BufReader(R), ...)
new : (fn(inner : R) -> Self)

Wrap inner with the default 4096-byte buffer.

Returns: Self

with_capacity : (fn(inner : R, capacity : usize) -> Self)

Wrap inner with a capacity-byte buffer.

Returns: Self

buffered : (fn(self : Self) -> usize)

Number of bytes currently buffered.

Returns: usize

_fill : (fn(self : Self, io : Io) -> Impl(Future(usize, IoExn)))

Refill the internal buffer from the inner reader. Resolves to the number of bytes now buffered; 0 means the inner reader hit EOF.

Returns: Impl(Future(usize, IoExn))

read_line : (fn(self : Self, io : Io) -> Impl(Future(Option(String), IoExn)))

Read one line (up to and including \n; the \n is consumed and NOT included). Resolves to .None on clean EOF with no pending bytes. The bytes are handed back UNVALIDATED via the unchecked constructor — framing protocols read ASCII headers; validate with String.from_utf8 where the content is untrusted.

Returns: Impl(Future(Option(String), IoExn))

read_exact : (fn(self : Self, n : usize, io : Io) -> Impl(Future(Option(ArrayList(u8)), IoExn)))

Read EXACTLY n bytes, looping over short reads — the operation a framed protocol needs (Content-Length: N then N body bytes). Resolves to .None when EOF arrives first; the partial bytes are discarded with the broken frame.

Returns: Impl(Future(Option(ArrayList(u8)), IoExn))

impl(generic(R : Type), where(R <: Reader), BufReader(R), Reader(...))
read : (fn(self : Self, buf : *u8, size : usize, io : Io) -> Impl(Future(usize, 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
buf*u8
sizeusize
ioIo

Returns: Impl(Future(usize, IoExn))

BufWriter type-function
fn(W : Type) -> Type

Buffered writer over any Writer.

Type Parameters

NameTypeNotes
WTypecomptime

Trait Implementations

impl(generic(W : Type), where(W <: Writer), BufWriter(W), ...)
new : (fn(inner : W) -> Self)

Wrap inner with the default 4096-byte buffer.

Returns: Self

with_capacity : (fn(inner : W, capacity : usize) -> Self)

Wrap inner with a capacity-byte buffer.

Returns: Self

buffered : (fn(self : Self) -> usize)

Number of bytes currently buffered.

Returns: usize

write_string : (fn(self : Self, s : String, io : Io) -> Impl(Future(unit, IoExn)))

Buffer a String's bytes. The pointer-taking write is the trait's primitive; this is the form callers actually want, and the fd-based std/sys/bufio writer this module replaced had it — dropping it would have made "write a string to a buffered writer" a pointer exercise.

Returns: Impl(Future(unit, IoExn))

write_bytes : (fn(self : Self, data : ArrayList(u8), io : Io) -> Impl(Future(unit, IoExn)))

Buffer an ArrayList(u8). Companion to write_string, same rationale.

Returns: Impl(Future(unit, IoExn))

_flush_buf : (fn(self : Self, io : Io) -> Impl(Future(unit, IoExn)))

Deliver the buffered bytes to the inner writer (without flushing IT).

Returns: Impl(Future(unit, IoExn))

impl(generic(W : Type), where(W <: Writer), BufWriter(W), Writer(...))
write : (fn(self : Self, buf : *u8, size : usize, io : Io) -> Impl(Future(usize, 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
buf*u8
sizeusize
ioIo

Returns: Impl(Future(usize, IoExn))

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

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

Parameters

NameTypeNotes
ioIo

Returns: Impl(Future(unit, IoExn))