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`: cleanup garantito

defer posticipa l'esecuzione di una chiamata fino al return della funzione chiamante. Si esegue anche se la funzione esce per panic. Più defer nella stessa funzione si eseguono in ordine LIFO (Last-In, First-Out).

È il meccanismo idiomatico per la pulizia delle risorse: chiusura file, unlock di mutex, rilascio di connessioni, ripristino dello stato.

Sintassi e use case canonico

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

Tipici: f.Close(), mu.Unlock(), db.Close(), tx.Rollback(), recover().

Argomenti valutati subito, esecuzione posticipata

Quando scrivi defer foo(x), x viene valutato al momento del defer, non al momento dell'esecuzione:

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

Ordine LIFO

I defer si impilano e si eseguono dal più recente:

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

È coerente con la semantica delle risorse: l'ultima aperta è la prima da chiudere.

defer + recover: gestire panic

defer è l'unico modo per intercettare un panic:

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

Pitfall: defer in loop

Mettere defer f.Close() in un loop accumula defer fino alla fine della funzione, non di ogni iterazione. Su un loop lungo apri tanti file e li chiudi solo all'uscita: rischio leak di file descriptor.

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
}

Prova tu

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

Stampa 'inizio', poi differisci la stampa di 'fine' con defer, poi stampa 'mezzo'. L'output deve essere: inizio / mezzo / fine.

Cargando editor...
Mostrar pista

Il defer si esegue quando main() sta per ritornare.

Solución disponible después de 3 intentos

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

Scrivi tre defer consecutivi che stampano 1, 2 e 3 in quest'ordine nel codice. L'output sarà 3 / 2 / 1 (LIFO).

Cargando editor...
Mostrar pista

Tre defer in sequenza; ricordati che si eseguono in ordine inverso.

Solución disponible después de 3 intentos

Cuestionario#go.m3.l5.e3
Listo

Cosa stampa? (attenzione: gli argomenti del defer sono valutati subito)

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

Recap

  • defer call() posticipa l'esecuzione fino al return della funzione.
  • Più defer = ordine LIFO.
  • Argomenti valutati subito, esecuzione posticipata.
  • Usato per cleanup deterministico: file, mutex, connessioni.
  • defer + recover() intercetta un panic (raro, ai confini).
  • Mai defer dentro un loop lungo: accumula fino al return della funzione esterna.