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
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // chiamata SEMPRE all'uscita della funzione
// ... usa f liberamente ...
return nilTipici: 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:
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:
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:
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.
// 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
Stampa 'inizio', poi differisci la stampa di 'fine' con defer, poi stampa 'mezzo'. L'output deve essere: inizio / mezzo / fine.
Mostra suggerimento
Il defer si esegue quando main() sta per ritornare.
Soluzione disponibile dopo 3 tentativi
Scrivi tre defer consecutivi che stampano 1, 2 e 3 in quest'ordine nel codice. L'output sarà 3 / 2 / 1 (LIFO).
Mostra suggerimento
Tre defer in sequenza; ricordati che si eseguono in ordine inverso.
Soluzione disponibile dopo 3 tentativi
Cosa stampa? (attenzione: gli argomenti del defer sono valutati subito)
x := 10
defer fmt.Println(x)
x = 99Recap
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 unpanic(raro, ai confini).- Mai
deferdentro un loop lungo: accumula fino al return della funzione esterna.