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
| Concept | In Go |
|---|---|
| Schedule cleanup | defer call() — runs on return, any path |
| Order | LIFO (reverse of declaration) |
| Modify return value | A deferred closure can rewrite a named return |
| Raise / stop a panic | panic(v) / recover() inside a defer |
| Panic → error | defer func(){ if r := recover(); r != nil { err = ... } }() |
Next: 12 — Capstone.