Saltar al contenido principal
eLearner.app
Módulo 3 · Lección 5 de 515/50 en el curso~12 min
Lecciones del módulo (5/5)

defer: limpieza garantizada

defer pospone la ejecución de una llamada hasta la función adjunta devoluciones. Se ejecuta incluso si la función sale por pánico. Múltiples defer en la misma función, ejecute en orden LIFO (último en entrar, primero en salir).

Es el mecanismo idiomático para limpieza de recursos: cerrar archivos, desbloquear mutex, liberar conexiones, restaurar el estado.

Sintaxis y caso de uso canónico

Go
f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()   // chiamata SEMPRE all'uscita della funzione

// ... usa f liberamente ...
return nil

Típico: f.Close(), mu.Unlock(), db.Close(), tx.Rollback(), CÓDIGOPH4.

Argumentos evaluados inmediatamente, ejecución pospuesta

Cuando escribe defer foo(x), x se evalúa en el momento de la defer, no en el momento de la ejecución:

Go
x := 10
defer fmt.Println(x)   // cattura 10
x = 99
// quando la funzione ritorna stampa "10", non "99"

Orden LIFO

Los diferidos se acumulan y ejecutan comenzando con el más reciente:

Go
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// output: 3, 2, 1

Esto es consistente con la semántica de recursos: el último abierto es el primero para estar cerrado.

defer + recover: manejo del pánico

defer es la única forma de interceptar un panic:

Go
func safe() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recuperato:", r)
        }
    }()
    panic("boom")
}

Error: defer en un bucle

Poner defer f.Close() en un bucle acumula diferimientos hasta el final de la función, no de cada iteración. En un bucle largo abres muchos archivos. y ciérrelos solo cuando la función salga: riesgo de fuga del descriptor de archivo.

Go
// MALE: defer accumulati
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close()   // chiude TUTTI alla fine della funzione esterna
    // ...
}

// MEGLIO: estrai in una funzione
for _, path := range paths {
    elabora(path)   // ogni chiamata ha il proprio scope di defer
}

Pruébalo

Ejercicio#go.m3.l5.e1
Intentos: 0Cargando...

Imprima 'inizio', luego posponga la impresión de 'bien' con aplazar, luego imprima 'mezzo'. La salida debe ser: inizio/mezzo/fine.

Cargando editor...
Mostrar pista

El aplazamiento se ejecuta cuando main() está a punto de regresar.

Solución disponible después de 3 intentos

Ejercicio#go.m3.l5.e2
Intentos: 0Cargando...

Escribe tres aplazamientos consecutivos que imprima 1, 2 y 3 en ese orden en el código. La salida será 3/2/1 (LIFO).

Cargando editor...
Mostrar pista

Tres diferidos en secuencia; Recuerde que se ejecutan en orden inverso.

Solución disponible después de 3 intentos

Cuestionario#go.m3.l5.e3
Listo

¿Qué imprime esto? (nota: los argumentos diferidos se evalúan inmediatamente)

Go
x := 10
defer fmt.Println(x)
x = 99
Opciones de respuesta

Resumen

  • defer call() pospone la ejecución hasta que regrese la función.
  • Múltiples aplazamientos = orden LIFO.
  • Argumentos evaluados inmediatamente, ejecución pospuesta.
  • Se utiliza para limpieza determinista: archivos, mutex, conexiones.
  • defer + recover() intercepta un panic (raro, en los límites).
  • Nunca defer dentro de un bucle largo: se acumula hasta que regresa la función externa.