Passer au contenu principal
eLearner.app
Module 3 · Leçon 5 sur 515/50 dans le cours~12 min
Leçons du module (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

Exercice#go.m3.l5.e1
Tentatives : 0Chargement…

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

Chargement de l'éditeur…
Afficher l'indice

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

Solution disponible après 3 tentatives

Exercice#go.m3.l5.e2
Tentatives : 0Chargement…

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

Chargement de l'éditeur…
Afficher l'indice

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

Solution disponible après 3 tentatives

Quiz#go.m3.l5.e3
Prêt

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

Go
x := 10
defer fmt.Println(x)
x = 99
Options de réponse

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.