# `errdefer`

### `errdefer`

`errdefer` is a cleanup tool.

It means:

```text
run this cleanup code only if the function returns with an error
```

It is similar to `defer`, but more specific.

`defer` runs when the function exits for any reason.

`errdefer` runs only when the function exits because of an error.

### The Problem `errdefer` Solves

Many functions build something step by step.

For example, a function might:

```text
allocate memory
open a file
initialize a resource
do more work
return the finished result
```

If every step succeeds, the caller receives the finished result.

But if one step fails halfway through, the function must clean up the work it already did.

That is where `errdefer` is useful.

### A Simple Allocation Example

Suppose we allocate a buffer and return it:

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

fn makeBuffer(allocator: std.mem.Allocator) ![]u8 {
    const buffer = try allocator.alloc(u8, 1024);
    errdefer allocator.free(buffer);

    // more work could fail here

    return buffer;
}
```

This line allocates memory:

```zig
const buffer = try allocator.alloc(u8, 1024);
```

If allocation fails, the function returns immediately. No cleanup is needed, because no buffer was created.

This line registers error cleanup:

```zig
errdefer allocator.free(buffer);
```

Now Zig knows:

```text
if this function later returns an error, free buffer
```

If the function succeeds, `errdefer` does not run. That is correct, because the function returns `buffer` to the caller. The caller now owns it.

### Why `defer` Would Be Wrong Here

You might think we should write:

```zig
defer allocator.free(buffer);
```

But that would free the buffer even when the function succeeds.

```zig
fn makeBuffer(allocator: std.mem.Allocator) ![]u8 {
    const buffer = try allocator.alloc(u8, 1024);
    defer allocator.free(buffer);

    return buffer;
}
```

This is wrong.

The function returns a slice pointing to memory that has already been freed. The caller receives a dangling slice.

Use `defer` when the current function should always clean up.

Use `errdefer` when the current function should clean up only if construction fails.

### Building a Resource in Steps

Here is a more realistic example:

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

const Resource = struct {
    name: []u8,
    data: []u8,
};

fn createResource(
    allocator: std.mem.Allocator,
    name: []const u8,
) !Resource {
    const owned_name = try allocator.dupe(u8, name);
    errdefer allocator.free(owned_name);

    const data = try allocator.alloc(u8, 4096);
    errdefer allocator.free(data);

    return Resource{
        .name = owned_name,
        .data = data,
    };
}
```

This function creates two owned pieces of memory.

First:

```zig
const owned_name = try allocator.dupe(u8, name);
errdefer allocator.free(owned_name);
```

If later work fails, `owned_name` must be freed.

Then:

```zig
const data = try allocator.alloc(u8, 4096);
errdefer allocator.free(data);
```

If later work fails, `data` must also be freed.

If both allocations succeed, the function returns a `Resource`. The caller owns both fields.

### Cleanup Runs in Reverse Order

Like `defer`, multiple `errdefer` statements run in reverse order.

```zig
fn example(allocator: std.mem.Allocator) !void {
    const a = try allocator.alloc(u8, 10);
    errdefer allocator.free(a);

    const b = try allocator.alloc(u8, 20);
    errdefer allocator.free(b);

    return error.Failed;
}
```

When the function returns `error.Failed`, cleanup runs like this:

```text
free b
free a
```

This is usually what you want.

Resources are cleaned up in the opposite order from how they were acquired.

### `errdefer` and Ownership

`errdefer` is closely tied to ownership.

When a function is building a value to return, there is a period where the function owns the parts.

If construction fails, the function must destroy those parts.

If construction succeeds, ownership moves to the caller.

`errdefer` expresses exactly that pattern:

```text
I own this for now.
If I fail, I clean it up.
If I succeed, I give it away.
```

This is one of the clearest uses of `errdefer`.

### `errdefer` with Files

`errdefer` is not only for memory.

It works with any cleanup code.

```zig
fn createOutputFile(path: []const u8) !std.fs.File {
    const file = try std.fs.cwd().createFile(path, .{});
    errdefer file.close();

    // more setup could fail here

    return file;
}
```

If file creation succeeds but later setup fails, `file.close()` runs.

If the function succeeds, the file is returned to the caller. The caller must close it.

### `defer` and `errdefer` Together

Sometimes you use both.

```zig
fn writeMessage(path: []const u8, message: []const u8) !void {
    const file = try std.fs.cwd().createFile(path, .{});
    defer file.close();

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

Here `defer` is correct. The function does not return the file. The current function owns the file for its whole lifetime and should always close it.

Now compare:

```zig
fn openMessageFile(path: []const u8) !std.fs.File {
    const file = try std.fs.cwd().openFile(path, .{});
    errdefer file.close();

    // validate file before returning it

    return file;
}
```

Here `errdefer` is correct. If validation fails, close the file. If validation succeeds, return the file to the caller.

### `errdefer` Can Capture the Error

`errdefer` can also capture the error that caused the function to fail.

```zig
fn run() !void {
    errdefer |err| {
        std.debug.print("failed with error: {}\n", .{err});
    }

    return error.SomethingWentWrong;
}
```

If `run` returns an error, the `errdefer` block runs and receives that error.

This is useful for logging or diagnostics.

Use it carefully. Cleanup code should usually be simple. If error cleanup becomes large, consider moving it into a helper function.

### `errdefer` Does Not Run on Success

This is the most important rule.

```zig
fn example() !void {
    errdefer std.debug.print("cleanup\n", .{});

    return;
}
```

The function succeeds. The `errdefer` does not run.

But here:

```zig
fn example() !void {
    errdefer std.debug.print("cleanup\n", .{});

    return error.Failed;
}
```

The function fails. The `errdefer` runs.

### `errdefer` Does Not Replace Good Design

`errdefer` is useful, but it does not remove the need to design ownership clearly.

When writing a function, ask:

Who owns this value right now?

Who frees it if the next step fails?

Who owns it after the function returns successfully?

If the answer is “this function owns it until success,” `errdefer` is often the right tool.

### A Common Beginner Mistake

A common mistake is using `errdefer` when `defer` is needed.

For example:

```zig
fn printFile(path: []const u8) !void {
    const file = try std.fs.cwd().openFile(path, .{});
    errdefer file.close();

    // read and print the file
}
```

This is probably wrong.

If the function succeeds, `errdefer` does not run, so the file stays open.

Since this function does not return the file to the caller, it should use `defer`:

```zig
fn printFile(path: []const u8) !void {
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close();

    // read and print the file
}
```

Use `defer` for cleanup that must always happen.

Use `errdefer` for cleanup that should happen only when success does not happen.

### The Core Idea

`errdefer` means:

```text
if this function fails later, run this cleanup
```

It is mainly used when building owned resources step by step.

If the function succeeds, ownership usually moves to the caller, so `errdefer` stays silent.

If the function fails, `errdefer` prevents leaks and half-built resources.

