Defer es un mecanismo único de Go que permite ejecutar una función después de que finalice la función actual, incluso si se produce un panic. Históricamente, esto es análogo a las construcciones on-exit, pero en Go se implementa como una pila de llamadas deferred. Un matiz importante es que los parámetros de la función deferred se calculan en el momento de la declaración de defer, ¡no en el momento de su real llamada!
Problema — comportamiento no obvio: se podría esperar que los parámetros se pasen al activarse el defer, pero no es así. Esto a menudo conduce a errores al trabajar con variables mutables o externas.
Solución — tener siempre en cuenta que los parámetros se calculan de inmediato y que el efecto de la llamada deferred sucede después.
Ejemplo de código:
func f() { x := 5 defer fmt.Println(x) x = 10 } // Imprimirá: 5, y no 10
Características clave:
¿Qué imprimirá el siguiente código? ¿Por qué?
func main() { i := 0 defer fmt.Println(i) i = 1 }
Respuesta: imprimirá 0. El argumento de la función fmt.Println se guarda inmediatamente al declarar el defer.
¿Afacta el cambio de la variable después de declarar el defer a la transmisión de su valor a la función?
No, no afecta — el cálculo ocurre al declarar el defer:
defer fmt.Println(x) // El valor de x se guarda ahora, no después
¿Se puede hacer un defer para imprimir el último estado de la variable?
Sí, mediante una función anónima (closure):
defer func() { fmt.Println(x) }() // capturará el valor actual de x en el momento de la llamada deferred
El código se llama así:
var f *os.File // ... defer f.Close()
Pero f se asigna más tarde, ¡por lo que se produce un panic al llamar a un puntero nil!
Ventajas:
Desventajas:
Envolver la acción de limpieza a través de una función deferred anónima con una verificación:
defer func() { if f != nil { f.Close() } }()
Ventajas:
Desventajas: