Defer & Errdefer
Defer
Runs a statement when the enclosing scope exits — whether by normal return, early return, or error propagation.
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):
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:
fn init() -> Resource or DeviceErr {
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; };
}What a defer can hold
A defer runs while its scope is already exiting, so its statement cannot leave: return, or return, and break or continue of a loop around the defer are compile errors in a defer or errdefer. A loop inside the deferred block can still use break and continue, and or { ... } works as long as it stays inside. The statement cannot be a declaration either (defer let x = f();): the binding would end with the defer.
defer {
for (i in 0..len) {
if (done(i)) { break; } // fine: leaves the defer's own loop
release(i);
}
}
// defer return 0; // error: no 'return' in a deferDefer vs errdefer
| Runs on success | Runs on error | |
|---|---|---|
defer | Yes | Yes |
errdefer | No | Yes |
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: