zig-skills/references/std-fs.md

6.4 KiB

File System API Reference (Zig 0.16.0)

Primary release-note source: https://ziglang.org/download/0.16.0/release-notes.html

Zig 0.16 migrates file-system operations to std.Io. Use std.Io.Dir, std.Io.File, and an explicit std.Io parameter. std.fs is now mostly path helpers and deprecated compatibility names.

Core Types

std.Io.Dir       // Directory handle and file-system operations
std.Io.File      // File handle, readers, writers, stat, locking
std.Io.File.Atomic
std.Io.File.MemoryMap
std.Io.Dir.path  // Path helpers; std.fs.path is deprecated compatibility

Current Directory

const cwd = std.Io.Dir.cwd();

Do not use old std.fs.cwd() in new 0.16 code.

Opening Files

const file = try std.Io.Dir.cwd().openFile(io, "data.txt", .{});
defer file.close(io);

const rw = try std.Io.Dir.cwd().openFile(io, "data.txt", .{
    .mode = .read_write,
});
defer rw.close(io);

Creating Files

const file = try std.Io.Dir.cwd().createFile(io, "out.txt", .{});
defer file.close(io);

Common options remain conceptually similar: truncate, exclusive create, read access, mode/permissions, and locking where supported.

Reading Files

Stream With Reader

const file = try std.Io.Dir.cwd().openFile(io, "data.txt", .{});
defer file.close(io);

var buffer: [4096]u8 = undefined;
var file_reader = file.reader(io, &buffer);
const reader = &file_reader.interface;

while (try reader.takeDelimiter('\n')) |line| {
    _ = line;
}

Allocate Whole File

const bytes = try std.Io.Dir.cwd().readFileAlloc(io, "data.txt", allocator, .limited(1024 * 1024));
defer allocator.free(bytes);

The limit uses std.Io.Limit. Hitting the limit returns error.StreamTooLong.

Read To End From Existing File

var buffer: [4096]u8 = undefined;
var file_reader = file.reader(io, &buffer);
const bytes = try file_reader.interface.allocRemaining(allocator, .limited(max_size));
defer allocator.free(bytes);

Writing Files

Stream With Writer

const file = try std.Io.Dir.cwd().createFile(io, "out.txt", .{});
defer file.close(io);

var buffer: [4096]u8 = undefined;
var file_writer = file.writer(io, &buffer);
const writer = &file_writer.interface;

try writer.print("value={d}\n", .{42});
try writer.writeAll("raw bytes\n");
try writer.flush();

Write Whole File

try std.Io.Dir.cwd().writeFile(io, .{
    .sub_path = "out.txt",
    .data = "hello\n",
});

Standard I/O

var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buffer);
try stdout_writer.interface.print("hello\n", .{});
try stdout_writer.interface.flush();

var stderr_buffer: [4096]u8 = undefined;
var stderr_writer = std.Io.File.stderr().writer(io, &stderr_buffer);
try stderr_writer.interface.print("error: {s}\n", .{"message"});
try stderr_writer.interface.flush();
var stdin_buffer: [4096]u8 = undefined;
var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buffer);

if (try stdin_reader.interface.takeDelimiter('\n')) |line| {
    _ = line;
}

Directories

var dir = try std.Io.Dir.cwd().openDir(io, "assets", .{});
defer dir.close(io);

try std.Io.Dir.cwd().createDir(io, "new-dir", .default_dir);
try std.Io.Dir.cwd().createDirPath(io, "path/to/nested");

Use openDir options for iteration/access/no-follow behavior as needed.

Walking

Use walk for full recursive walks and walkSelectively when you want to decide which directories to enter.

var walker = try dir.walk(allocator);
defer walker.deinit();

while (try walker.next(io)) |entry| {
    _ = entry;
}
var walker = try dir.walkSelectively(allocator);
defer walker.deinit();

while (try walker.next(io)) |entry| {
    if (entry.kind == .directory and shouldEnter(entry)) {
        try walker.enter(io, entry);
    }
}

Metadata

const stat = try file.stat(io);

const size = stat.size;
const kind = stat.kind;
const modified = stat.mtime;
const accessed = stat.atime orelse return error.FileAccessTimeUnavailable;
_ = .{ size, kind, modified, accessed };

atime is optional in 0.16.

Setting timestamps uses structured options:

try file.setTimestamps(io, .{
    .access_timestamp = .init(stat.atime),
    .modify_timestamp = .init(stat.mtime),
});

Paths

std.Io.Dir.path / std.fs.path functions handle Windows paths more consistently in 0.16.

relative, relativeWindows, and relativePosix are pure: pass the current directory and optional environment map instead of letting the function query the OS.

const cwd_path = try std.process.currentPathAlloc(io, allocator);
defer allocator.free(cwd_path);

const rel = try std.fs.path.relative(allocator, cwd_path, environ_map, from, to);
defer allocator.free(rel);

Atomic Files

Use std.Io.File.Atomic or directory atomic helpers instead of hand-rolled random temporary names. The 0.16 implementation is routed through std.Io, including entropy.

Absolute Operations

Many old std.fs.*Absolute functions moved to std.Io.Dir.*Absolute, for example:

  • std.fs.openFileAbsolute -> std.Io.Dir.openFileAbsolute
  • std.fs.createFileAbsolute -> std.Io.Dir.createFileAbsolute
  • std.fs.deleteFileAbsolute -> std.Io.Dir.deleteFileAbsolute
  • std.fs.renameAbsolute -> std.Io.Dir.renameAbsolute
  • std.fs.accessAbsolute -> std.Io.Dir.accessAbsolute

Many Z and W path-specific helpers were removed.

Migration Map

Old 0.15-style API Zig 0.16 API
std.fs.cwd() std.Io.Dir.cwd()
std.fs.File.stdout() std.Io.File.stdout()
file.close() file.close(io)
dir.close() dir.close(io)
file.reader(&buf) file.reader(io, &buf)
file.writer(&buf) file.writer(io, &buf)
dir.readFileAlloc(allocator, path, max) dir.readFileAlloc(io, path, allocator, .limited(max))
file.readToEndAlloc(allocator, max) file.reader(io, &buf).interface.allocRemaining(allocator, .limited(max))
std.process.getCwdAlloc(allocator) std.process.currentPathAlloc(io, allocator)

Application Guidance

  • Prefer an application's virtual or asset file-system abstraction for runtime reads when one exists.
  • New direct file operations still need an io parameter at the API boundary.
  • Writes should be explicit and use std.Io.Dir / std.Io.File until a project abstraction owns write policy.
  • Avoid local fallback Io.Threaded construction in low-level file helpers; pass or store io.