Vai al contenuto
eLearner.app
Modulo 3 · Lezione 5 di 515/50 nel corso~12 min
Lezioni del modulo (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

Esercizio#go.m3.l5.e1
Tentativi: 0Caricamento…

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

Caricamento editor…
Mostra suggerimento

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

Soluzione disponibile dopo 3 tentativi

Esercizio#go.m3.l5.e2
Tentativi: 0Caricamento…

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

Caricamento editor…
Mostra suggerimento

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

Soluzione disponibile dopo 3 tentativi

Quiz#go.m3.l5.e3
Pronto

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

Go
x := 10
defer fmt.Println(x)
x = 99
Opzioni di risposta

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.