Stdio-zig Buffered stdin/stdout reading and writing for Zig without the boilerplate

stdio-zig

A teensy library that wraps stdin/stdout behind a clean, buffered API for Zig’s new std.Io interface (0.16+).
https://github.com/tacheraSasi/stdio-zig

In all my CLI projects I found myself writing the same three things over and over: allocate buffers, wire up stdout/stdin readers and writers, and remember to flush. This puts all of that in one Console struct declare two buffers, call init, and go.

Usage

const std = @import("std");
const stdio = @import("stdio");

pub fn main(init: std.process.Init) !void {
    const io = init.io;
    var write_buf: [4096]u8 = undefined;
    var read_buf: [4096]u8 = undefined;

    var console: stdio.Console = undefined;
    console.init(io, &write_buf, &read_buf);

    try console.printLine("What's your name?", .{});
    const name = try console.readLine();
    try console.printLine("Hello, {s}!", .{name});
}

The design is intentionally minimal: the entire library is one console.zig file with no dependencies beyond std. The struct holds both the concrete File.Writer/File.Reader (with their per-instance state) and an *Io.Writer/*Io.Reader interface pointer, giving callers the option to work through the abstract interface or access the underlying file handle if needed.

Recently I added a few quality-of-life methods prompt, confirm, readLineTrim, readLineOrNull that codify patterns I was tired of typing in every interactive script. readLineOrNull in particular makes EOF-handling loops much cleaner than catching error.EndOfStream at every call site.

One thing I learned while building this: Zig’s Io.Writer handles buffering internally via its buffer/end/vtable-drain pattern, so there’s no need for a separate buffered-writer abstraction. The library is essentially just wiring and naming conventions on top of that built-in machinery.

Supported Zig versions

0.16.x, 0.17.x-dev

6 Likes