Skip to content

Zig 0.16 API Gotchas

Janus targets: 0.16.0-dev.2964+10256e1b8
Last updated: 2026-03-30

When writing or modifying Janus compiler code (Zig), these patterns will cause compile errors. This is a living document—add new gotchas as we discover them.


Zig 0.16 reorganized the I/O module. The lowercase io is gone.

// ❌ DOES NOT COMPILE
const stdout = std.io.getStdOut().writer();
// ✅ CORRECT
const stdout = std.Io.File.stdout().writer();

Zig 0.16 removed .writer() from ArrayList. Use the BufWriter pattern instead.

// ❌ DOES NOT COMPILE
var output = std.ArrayList(u8).init(allocator);
defer output.deinit();
const writer = output.writer();
// ✅ CORRECT — BufWriter adapter
const BufWriter = struct {
buf: *std.ArrayList(u8),
alloc: std.mem.Allocator,
pub fn print(self: BufWriter, comptime fmt: []const u8, args: anytype) !void {
var tmp: [4096]u8 = undefined;
const result = std.fmt.bufPrint(&tmp, fmt, args) catch return error.NoSpaceLeft;
try self.buf.appendSlice(self.alloc, result);
}
pub fn writeAll(self: BufWriter, data: []const u8) !void {
try self.buf.appendSlice(self.alloc, data);
}
};
var output: std.ArrayList(u8) = .empty;
try output.ensureTotalCapacity(allocator, 4096);
defer output.deinit(allocator);
const writer = bufWriter(&output, allocator);
fn bufWriter(buf: *std.ArrayList(u8), allocator: std.mem.Allocator) BufWriter {
return BufWriter{ .buf = buf, .alloc = allocator };
}

Existing reference implementations:

  • compiler/inspect/dumper.zigBufWriter struct
  • cmd/janusd/json_helpers.zigArrayListWriter

The .init() constructor was removed in favor of .empty literal.

// ❌ DOES NOT COMPILE
var list = std.ArrayList(u8).init(allocator);
// ✅ CORRECT
var list: std.ArrayList(u8) = .empty;
try list.ensureTotalCapacity(allocator, 256);
defer list.deinit(allocator);

4. std.fs.cwd() File Operations → compat_fs

Section titled “4. std.fs.cwd() File Operations → compat_fs”

Direct filesystem operations on std.fs.cwd() don’t work the same way. Use the Janus compat_fs module instead.

// ❌ DOES NOT COMPILE
const source = std.fs.cwd().readFileAlloc(allocator, path, size);
std.fs.cwd().writeFile(path, data);
// ✅ CORRECT
const source = compat_fs.readFileAlloc(allocator, path, size);
try compat_fs.writeFile(.{ .sub_path = path, .data = data });

Note: The compat_fs module must be imported explicitly:

const compat_fs = @import("compat_fs");

And added to the module’s build.zig imports:

janus_cli.root_module.addImport("compat_fs", b.addModule("compat_fs", .{
.root_source_file = b.path("runtime/compat/fs.zig"),
.target = target,
.optimize = optimize,
}));

ArrayList.deinit() now takes an allocator parameter.

// ❌ COMPILE WARNING (will change)
defer list.deinit();
// ✅ CORRECT
defer list.deinit(allocator);

All mutation operations on ArrayList now require explicit allocator.

// ❌ DOES NOT COMPILE
try list.append(item);
// ✅ CORRECT
try list.append(allocator, item);
// ❌ DOES NOT COMPILE
try list.appendSlice(items);
// ✅ CORRECT
try list.appendSlice(allocator, items);

Passing enum values directly to {s} format specifier doesn’t work.

// ❌ DOES NOT COMPILE — JanusType is an enum
try writer.print("type: {s}", .{some_janus_type});
// ✅ CORRECT — use @tagName()
try writer.print("type: {s}", .{@tagName(some_janus_type)});

std.Io is no longer a simple namespace—it requires an Io instance for many operations.

// ❌ WON'T WORK as expected
const file = std.Io.File.stdout();
// ✅ MAY NEED context depending on usage
// For simple stdout writing, prefer std.debug.print() instead
std.debug.print("{s}", .{output.items});

OperationOld (0.14)New (0.16)
Create ArrayList.init(allocator).empty + ensureTotalCapacity
Deallocatedeinit()deinit(allocator)
Append itemappend(item)append(allocator, item)
Get writer.writer()Use BufWriter adapter
Stdoutstd.io.getStdOut()std.Io.File.stdout()
Read filecwd().readFileAlloc()compat_fs.readFileAlloc()
Write filecwd().writeFile()compat_fs.writeFile(.{.sub_path, .data})