# Error Handling in I/O

### Error Handling in I/O

Input and output can fail.

A file may not exist. A disk may be full. A directory may be missing. A program may not have permission to read or write. A pipe may be closed by the program on the other end.

Zig makes these cases part of the type.

```zig
pub fn main() !void {
    const cwd = std.fs.cwd();

    const file = try cwd.openFile("input.txt", .{});
    defer file.close();

    var buffer: [128]u8 = undefined;
    const n = try file.read(&buffer);

    try std.io.getStdOut().writeAll(buffer[0..n]);
}
```

The return type is:

```zig
!void
```

This means the function either completes normally with `void`, or returns an error.

The expression:

```zig
try cwd.openFile("input.txt", .{})
```

means: if opening the file succeeds, use the file. If it fails, return the error from the current function.

This is the common style for small programs. It keeps the main path clear, but does not discard errors.

Sometimes a program should handle an error directly.

```zig
const file = cwd.openFile("input.txt", .{}) catch |err| {
    try std.io.getStdErr().writer().print(
        "cannot open input.txt: {}\n",
        .{err},
    );
    return err;
};
```

The `catch` branch receives the error value in `err`.

This lets the program print a better diagnostic before returning.

Different errors can be handled differently with `switch`.

```zig
const file = cwd.openFile("input.txt", .{}) catch |err| switch (err) {
    error.FileNotFound => {
        try std.io.getStdErr().writeAll(
            "input.txt does not exist\n",
        );
        return err;
    },
    error.AccessDenied => {
        try std.io.getStdErr().writeAll(
            "permission denied\n",
        );
        return err;
    },
    else => return err,
};
```

This is useful near the boundary of a program, where errors become messages for users.

Inside lower-level functions, returning errors is often better.

```zig
fn copyFile(src_name: []const u8, dst_name: []const u8) !void {
    const cwd = std.fs.cwd();

    const src = try cwd.openFile(src_name, .{});
    defer src.close();

    const dst = try cwd.createFile(dst_name, .{});
    defer dst.close();

    var buffer: [4096]u8 = undefined;

    while (true) {
        const n = try src.read(&buffer);
        if (n == 0) break;

        try dst.writeAll(buffer[0..n]);
    }
}
```

The function does not decide how to report errors. It only says that copying may fail.

The caller decides what to do.

```zig
pub fn main() !void {
    copyFile("input.txt", "output.txt") catch |err| {
        try std.io.getStdErr().writer().print(
            "copy failed: {}\n",
            .{err},
        );
        return err;
    };
}
```

This separation is important. Low-level code should usually preserve the error. Top-level code should usually explain the error.

Cleanup must still happen when errors occur. `defer` handles normal cleanup.

```zig
const file = try cwd.openFile("input.txt", .{});
defer file.close();
```

If a later `try` returns an error, `file.close()` still runs.

For cleanup that should happen only on error, use `errdefer`.

```zig
const dst = try cwd.createFile("output.txt", .{});
errdefer cwd.deleteFile("output.txt") catch {};
defer dst.close();
```

Here, if the function fails after creating `output.txt`, the partial file is removed. If the function succeeds, `errdefer` does not run.

This pattern is common when writing output files:

```zig
fn writeCompleteFile(name: []const u8, data: []const u8) !void {
    const cwd = std.fs.cwd();

    const file = try cwd.createFile(name, .{});
    errdefer cwd.deleteFile(name) catch {};
    defer file.close();

    try file.writeAll(data);
}
```

If `writeAll` fails, the incomplete file is deleted.

I/O code should follow a few rules:

1. return errors from low-level functions
2. print diagnostics at program boundaries
3. use `defer` for resource cleanup
4. use `errdefer` for rollback
5. never assume a file operation succeeds

Exercise 13-21. Modify the file copy program so it prints `file not found` for `error.FileNotFound`.

Exercise 13-22. Write a function that opens a file and returns its first byte.

Exercise 13-23. Change the function so it returns `null` for an empty file.

Exercise 13-24. Write an output function that deletes the output file if writing fails.

