# 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 ```zig 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 ```zig const cwd = std.Io.Dir.cwd(); ``` Do not use old `std.fs.cwd()` in new 0.16 code. ## Opening Files ```zig 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 ```zig 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 ```zig 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 ```zig 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 ```zig 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 ```zig 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 ```zig try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = "out.txt", .data = "hello\n", }); ``` ## Standard I/O ```zig 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(); ``` ```zig 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 ```zig 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. ```zig var walker = try dir.walk(allocator); defer walker.deinit(); while (try walker.next(io)) |entry| { _ = entry; } ``` ```zig 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 ```zig 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: ```zig 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. ```zig 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`.