Lesson 6 · 6 of 20
defer, panic and recover · Go
Postpone calls with defer, see in which order they run, and turn a panic into an ordinary error with recover, in Go.

`defer` postpones a call until the surrounding function returns, however it returns. In importFile, defer fmt.Println("close", name) is written right after opening: the cleanup sits next to the setup and is never forgotten, even if the function has several return statements.
Deferred calls are stacked and run in reverse order (last in, first out): that is why you see undo step 3, 2, 1 and then close. Arguments are evaluated when you write the `defer`, not when it runs: each step is fixed at the value it had then.
A `panic` is a failure that stops the program: here total / len(scores) causes one with an empty list, a division by zero. The panic travels up through the functions, running their defer calls; if nothing stops it, the program ends with an error message.
`recover` stops it, but only inside a deferred function. average defers an anonymous function that calls recover(): if there was a panic, it returns its value (runtime error: integer divide by zero) and the function turns it into an error. Because the results are named, (avg int, err error), the deferred function can change err before average returns.
The defer in main was written first and prints last. In Go, ordinary errors are returned as values; panic and recover are kept for programming mistakes, or to stop a server from going down because of a single request.
Code · main.go
package mainimport "fmt"func importFile(name string) { fmt.Println("open", name) defer fmt.Println("close", name) // runs when importFile returns for step := 1; step <= 3; step++ { defer fmt.Println(" undo step", step) // arguments are evaluated now } fmt.Println(" importing rows...")}func average(scores []int) (avg int, err error) { // recover turns a panic into an error defer func() { if r := recover(); r != nil { err = fmt.Errorf("recovered: %v", r) // sets the named result } }() total := 0 for _, s := range scores { total += s } return total / len(scores), nil // panics if scores is empty}func main() { defer fmt.Println("main: deferred until the very end") importFile("orders.csv") for _, scores := range [][]int{{90, 85, 95}, {}} { avg, err := average(scores) fmt.Println("average:", avg, "| error:", err) }}Output · go run main.go
open orders.csv importing rows... undo step 3 undo step 2 undo step 1 close orders.csv average: 90 | error: <nil> average: 0 | error: recovered: runtime error: integer divide by zero main: deferred until the very end
Key points
deferruns the call when the function returns.- Several
defercalls run in reverse order. - A
defer's arguments are evaluated when it is written. recoveronly works inside a deferred function.- With named results, a
defercan change what is returned.
Exercise
Replace the loop in importFile with defer func() { fmt.Println(" undo step", step) }() and see which number is printed now: the anonymous function reads step when it runs, not when it is deferred.