Defer & Errdefer

Defer

Runs a statement when the enclosing scope exits — whether by normal return, early return, or error propagation.

tape
fn read_file(path: string) -> string or IoError {
    let f = open(path) or return;
    defer close(f);

    // even if this fails and returns early, close(f) runs
    let data = read_all(f) or return;
    return data;
}

Multiple defers

Defers execute in reverse order (LIFO):

tape
fn setup() {
    let a = acquire_a();
    defer release_a(a);

    let b = acquire_b();
    defer release_b(b);

    // on scope exit: release_b runs first, then release_a
}

Errdefer

Runs only if the scope exits via an error return:

tape
fn init() -> Resource or Error {
    let mem = allocate(1024) or return;
    errdefer free(mem);  // only runs if we return an error below

    let handle = open_device() or return;
    // if open_device fails, mem is freed
    // if it succeeds, mem is NOT freed (caller owns it)

    return Resource { mem: mem; handle: handle; };
}

Defer vs errdefer

Runs on successRuns on error
deferYesYes
errdeferNoYes

Use defer for cleanup that always happens (close files, release locks). Use errdefer for cleanup that only happens on the failure path (free partially-constructed resources).

Last modified: