Zig

2026-09-01zig
Learning notes for zig programming language
const std = @import("std");
const Io  = std.Io;

Different ways to print

The methods mentioned here are by no means exhaustive.

Debug print

Writes to stderr and ignores errors (hence no try), bypasses the Io interface. Ideally we should delete every occurence of std.debug.print after debugging.

fn @"std-debug-print"() void
{
   std.debug.print("Hello, {s}!\n", .{"std.debug.print"});
}

Io.Writer

You'd probably want to watch this talk by Andrew Kelly and this post by Joe Mckay.

Io.File.stderr().writerStreaming(io, &buffer) returns Io.File.Writer and we can use its interface i.e. Io.Writer to actually write to stderr, the buffer we hand is managed through the interface (buffering in the interface). And don't forget to flush.

There are two modes streaming and no-streaming for Io.File.Writer. We should take the streaming variant for stdout/stderr(/stdin).
fn @"std-Io-Writer"(io: Io) !void
{
   var stderr_buf: [1024]u8 = undefined;
   var stderr_writer = Io.File.stderr().writerStreaming(io, &stderr_buf);
   const stderr = &stderr_writer.interface;
   defer stderr.flush() catch {};

   try stderr.print("Hello, {s}!\n", .{"std.Io.Writer"});
}

Thread-safe stderr

std.debug.lockStderr allows the caller to freely write to stderr until unlockStderr is called. During the lock, any std.Progress information is cleared from the standard stream. As std.debug.print, this ignores Io and should be deleted after debugging.

fn @"std-debug-lockStderr"() !void
{
   const stderr = std.debug.lockStderr(&.{}).terminal().writer;
   defer std.debug.unlockStderr();   // flushing is performed automatically

   try stderr.print("Hello, {s}!\n" , .{"std.debug.lockStderr"});
}

Alternatively we can use std.Io.lockStderr to perform application-level writes to stderr.

fn @"std-Io-lockStderr"(io: Io) !void
{
   var stderr_buf: [1024]u8 = undefined;
   var locked = try io.lockStderr(&stderr_buf, null);
   defer io.unlockStderr();

   const stderr = locked.terminal().writer;

   try stderr.print("Hello, {s}!\n" , .{"std.Io.lockStderr"});
}

To conclude, use debug for informal/debug-only diagnostics. Take a lock when e.g. a multi-write writer mustn't interleave with other writers.

ϗ zig build-exe zig-notes.zig && ./zig-notes
Hello, std.debug.print!
Hello, std.Io.Writer!
Hello, std.debug.lockStderr!
Hello, std.Io.lockStderr!
pub fn main(init: std.process.Init) !void
{
   @"std-debug-print"();
   try @"std-Io-Writer"(init.io);
   try @"std-debug-lockStderr"();
   try @"std-Io-lockStderr"(init.io);
}

TODO

Io.Reader & Io.Writer

reflexion