11 — defer, panic & recover

Go's cleanup mechanism (defer) and its last-resort failure mechanism (panic/recover). Every example is live — edit it and press Run.

defer runs LIFO; recover turns a panic into an error

A defer schedules a call to run when the function returns, on any path — Go's answer to C's goto cleanup. Multiple defers run in reverse order (last scheduled, first to run), and a deferred closure can modify a named return value. That last point is how you convert a panic into a returned error: recover, called inside a deferred function, stops the panic and hands you its value.

package main

import (
    "errors"
    "fmt"
)

// Three defers run in reverse, building ["third","second","first"].
func OrderOfCleanup() (order []string) {
    defer func() { order = append(order, "first") }()
    defer func() { order = append(order, "second") }()
    defer func() { order = append(order, "third") }()
    return
}

// a/b panics when b == 0; the deferred recover rewrites the named return err.
func SafeDivide(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = errors.New("division by zero")
        }
    }()
    return a / b, nil
}

func main() {
    fmt.Println(OrderOfCleanup())
    r, err := SafeDivide(10, 2)
    fmt.Println(r, err)
    r, err = SafeDivide(1, 0)
    fmt.Println(r, err)
}

Output:

[third second first]
5 <nil>
0 division by zero

recover only works inside a deferred function — anywhere else it returns nil and does nothing. And panic is not for ordinary failures: use an error return (module 05) for those, and reserve panics for truly unrecoverable states.

Recap

ConceptIn Go
Schedule cleanupdefer call() — runs on return, any path
OrderLIFO (reverse of declaration)
Modify return valueA deferred closure can rewrite a named return
Raise / stop a panicpanic(v) / recover() inside a defer
Panic → errordefer func(){ if r := recover(); r != nil { err = ... } }()

Next: 12 — Capstone.