⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Defer

const std = @import("std");
const expect = std.testing.expect;

test "defer runs at scope exit" {
    var x: i16 = 5;
    {
        defer x += 2;
        try expect(x == 5); // not yet
    }
    try expect(x == 7); // now
}

test "defers run in reverse order" {
    // Last registered runs first, so cleanup unwinds in the order things
    // were acquired.
    var order: [3]u8 = undefined;
    var index: usize = 0;
    {
        defer {
            order[index] = 1;
            index += 1;
        }
        defer {
            order[index] = 2;
            index += 1;
        }
        defer {
            order[index] = 3;
            index += 1;
        }
    }
    try expect(order[0] == 3);
    try expect(order[2] == 1);
}

fn mightFail(fail: bool) !u8 {
    var cleaned = false;
    // `errdefer` runs only when the function returns an error.
    errdefer cleaned = true;
    if (fail) return error.Nope;
    return @intFromBool(cleaned);
}

test "errdefer only fires on the error path" {
    try expect(try mightFail(false) == 0);
    try std.testing.expectError(error.Nope, mightFail(true));
}

defer schedules an expression to run when the enclosing scope exits: not the function, the scope. Every path counts: falling off the end, an early return, or an error propagating through try.

Reverse order is the point

Deferred statements run last-registered-first. That is what makes them compose: if you acquire A then B, cleanup happens B then A, which is almost always the correct unwinding order. Writing the release directly under the acquire keeps the pair visible in one glance:

const buf = try allocator.alloc(u8, n);
defer allocator.free(buf);

errdefer for the failure path only

errdefer runs only when the function returns an error. This is how you write a constructor that cleans up partial work without also undoing itself on success:

const thing = try create();
errdefer destroy(thing);   // only if a later step fails
try initialise(thing);
return thing;

With plain defer that would destroy the object you just successfully returned.