// `errdefer |err|` capture: the deferred cleanup sees WHICH error is // unwinding — `errdefer |err| ` (statement form) and // `errdefer |err| { … }` (block form). The error binds by value, as in // Zig (`|*err|` is rejected); plain `errdefer` stays capture-less and // composes freely with the captured forms. // // Lives in examples/register/ (post-v0.5 grammar — the frozen stage0 // bootstrap compiler cannot parse it; gated by `zig build fixpoint`). const InitError = error{ NoMemory, BadDevice } // The driver's failure breadcrumbs: which error unwound, and how many // cleanup steps ran on the way out. var last_failure u16 = 0 var unwound u16 = 0 fn classify(err InitError) u16 { if err == error.NoMemory { return 1 } return 2 } fn claim(slot u16) InitError!u16 { if slot == 0 { return error.BadDevice } return slot } // Bring up a device: each acquired resource registers its unwind step, // and the captured forms record the error that triggered the unwind. pub fn bringup(slot u16) InitError!u16 { id := try claim(slot) // The statement form: one deferred statement, the error in scope. errdefer |err| last_failure = classify(err) // The block form: a multi-step unwind, the same capture. errdefer |err| { last_failure = classify(err) unwound += 1 } // A capture-less errdefer runs the same way — there is no error to name. errdefer unwound += 1 _ = try claim(id + 1) return id }