# Filesystem

> Every disk operation now takes an Io.

Zig 0.16 moved the filesystem API onto `std.Io`. This is the change most likely
to make older filesystem examples fail to compile.

```zig
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    const io = init.io;
    const gpa = std.heap.page_allocator;

    var buf_out: [128]u8 = undefined;
    var stdout = std.Io.File.stdout().writerStreaming(io, &buf_out);
    const out = &stdout.interface;

    // `std.fs.cwd()` is now `std.Io.Dir.cwd()`, and every operation that
    // touches the disk takes the io instance explicitly.
    var dir = std.Io.Dir.cwd();

    // Create, write, close.
    const file = try dir.createFile(io, "example.txt", .{});
    defer file.close(io);

    var buf: [128]u8 = undefined;
    var writer = file.writerStreaming(io, &buf);
    try writer.interface.writeAll("written by zig\n");
    try writer.interface.flush();

    // Read the whole thing back.
    const contents = try dir.readFileAlloc(io, "example.txt", gpa, .limited(1 << 20));
    defer gpa.free(contents);
    std.debug.assert(std.mem.eql(u8, contents, "written by zig\n"));
    try out.print("read back {d} bytes\n", .{contents.len});

    // Walk a directory. The entry count is whatever happens to be in the
    // working directory, so count only the one file this program created:
    // anything derived from the directory's real contents would differ
    // between your machine and the build that verified this page.
    var iterable = try dir.openDir(io, ".", .{ .iterate = true });
    defer iterable.close(io);
    var it = iterable.iterate();
    var found = false;
    while (try it.next(io)) |entry| {
        if (std.mem.eql(u8, entry.name, "example.txt")) found = true;
    }
    try out.print("example.txt listed in cwd: {}\n", .{found});

    try dir.deleteFile(io, "example.txt");
    try out.flush();
}

// Marked `//! native`: CI builds this for the host and runs it against a real
// filesystem, so the round trip above is actually checked. It is the browser
// that cannot run it, because the WASI sandbox has no preopened directories.
```

*Built and run natively by CI against a real filesystem, so the round trip is checked. The browser WASI sandbox has no preopened directories. (`03-standard-library.filesystem`)*

## What moved

| Was | Now |
| --- | --- |
| `std.fs.cwd()` | `std.Io.Dir.cwd()` |
| `std.fs.File` | `std.Io.File` |
| `dir.openFile(path, .{})` | `dir.openFile(io, path, .{})` |
| `file.close()` | `file.close(io)` |

The pattern is uniform: anything that can block takes the `io` instance as its
first argument. You get that instance from `main`:

```zig
pub fn main(init: std.process.Init) !void {
    const io = init.io;
```

## Why

It is the same argument as `Allocator`. A function's signature now tells you
whether it performs I/O, and the *caller* decides how that I/O is executed
(threaded, evented, or something else) instead of the standard library
choosing on your behalf.

## Reading a whole file

```zig
const contents = try dir.readFileAlloc(io, path, gpa, .limited(1 << 20));
defer gpa.free(contents);
```

The limit is mandatory, not optional. There is no API that reads an
unbounded amount of untrusted input into memory by accident.

## Why this page does not run

Every other snippet on this site executes in your browser. This one cannot:
WASI programs can only touch directories the host explicitly preopens, and the
playground grants none. CI still compiles it, so if the filesystem API changes
again this page turns the build red. It just cannot be executed here.
