Lección 6 · 6 de 20
defer, panic y recover · Go
Aplace llamadas con defer, vea en qué orden se ejecutan y convierta un panic en un error normal con recover, en Go.

`defer` aplaza una llamada hasta que la función que la contiene termina, salga por donde salga. En importFile, defer fmt.Println("close", name) se escribe justo después de abrir: así el cierre queda al lado de la apertura y nunca se olvida, aunque la función tenga varios return.
Las llamadas aplazadas se apilan y se ejecutan en orden inverso (la última, la primera): por eso salen undo step 3, 2, 1 y, al final, close. Los argumentos se evalúan al hacer el `defer`, no al ejecutarlo: cada step queda fijado con su valor de ese momento.
Un `panic` es un fallo que detiene el programa: aquí lo provoca total / len(scores) con una lista vacía, una división entre cero. El panic sube por las funciones ejecutando sus defer; si nadie lo detiene, el programa termina con un mensaje de error.
`recover` lo detiene, pero solo dentro de una función aplazada. average aplaza una función anónima que llama a recover(): si hubo panic, devuelve su valor (runtime error: integer divide by zero) y la función lo convierte en un error. Como los resultados tienen nombre, (avg int, err error), la función aplazada puede cambiar err antes de que average devuelva.
El defer de main se escribió el primero y sale el último. En Go, los errores normales se devuelven como valores; panic y recover se reservan para fallos de programación o para que un servidor no se caiga entero por una sola petición.
Código · 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) }}Salida · 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
Para recordar
deferejecuta la llamada al terminar la función.- Varios
deferse ejecutan en orden inverso. - Los argumentos de un
deferse evalúan en el momento de escribirlo. recoversolo funciona dentro de una función aplazada.- Con resultados con nombre, un
deferpuede cambiar lo que se devuelve.
Ejercicio
Cambie el bucle de importFile por defer func() { fmt.Println(" undo step", step) }() y observe qué número sale ahora: la función anónima lee step al ejecutarse, no al aplazarse.