Direct naar de hoofdinhoud
Module 3 · Les 5 van 515/50 in de cursus~12 min
Modulelessen (5/5)

`defer`: gegarandeerde opruiming

defer stelt de uitvoering van een oproep ** uit tot de insluitende functie retourneert**. Het werkt zelfs als de functie via paniek wordt afgesloten. Meerdere defers in dezelfde functie uitvoeren in LIFO-volgorde (Last-In, First-Out).

Het is het idiomatische mechanisme voor opschonen van bronnen: bestanden sluiten, mutexen ontgrendelen, verbindingen vrijgeven, status herstellen.

Syntaxis en canonieke use-case

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

Typisch: f.Close(), mu.Unlock(), db.Close(), tx.Rollback(), BESCHERMD_INLINE_9.

Argumenten onmiddellijk geëvalueerd, uitvoering uitgesteld

Wanneer u defer foo(x) schrijft, wordt x geëvalueerd op het moment van de defer, niet tijdens uitvoering:

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

LIFO-bestelling

Stelt de stapel uit en voert deze uit, beginnend met de meest recente:

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

Dit komt overeen met de semantiek van bronnen: de laatst geopende is de eerste gesloten zijn.

defer + recover: omgaan met paniek

defer is de enige manier om een ​​panic te onderscheppen:

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

Valkuil: defer in een lus

Door defer f.Close() in een lus te plaatsen, worden uitstel ** verzameld tot het einde van de functie**, niet van elke iteratie. In een lange lus open je veel bestanden en sluit ze pas als de functie wordt afgesloten: risico op lekken van de bestandsdescriptor.

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
}

Probeer het

Oefening#go.m3.l5.e1
Pogingen: 0Laden…

Druk 'inizio' af, stel vervolgens de afdruk van 'fijn' uit met uitstel en druk vervolgens 'mezzo' af. De uitvoer moet zijn: inizio / mezzo / fine.

Editor laden…
Toon hint

Het uitstel wordt uitgevoerd wanneer main() op het punt staat terug te keren.

Oplossing beschikbaar na 3 pogingen

Oefening#go.m3.l5.e2
Pogingen: 0Laden…

Schrijf drie opeenvolgende defers met 1, 2 en 3 in die volgorde in de code. De uitvoer is 3 / 2 / 1 (LIFO).

Editor laden…
Toon hint

Drie uitstel achter elkaar; onthoud dat ze in omgekeerde volgorde worden uitgevoerd.

Oplossing beschikbaar na 3 pogingen

Quiz#go.m3.l5.e3
Klaar

Wat drukt dit af? (opmerking: uitstelargumenten worden onmiddellijk geëvalueerd)

Go
x := 10
defer fmt.Println(x)
x = 99
Antwoordmogelijkheden

Samenvatten

  • defer call() stelt de uitvoering uit totdat de functie terugkeert.
  • Meerdere uitstel = LIFO-bestelling.
  • Argumenten onmiddellijk geëvalueerd, uitvoering uitgesteld.
  • Gebruikt voor deterministische opschoning: bestanden, mutexen, verbindingen.
  • defer + recover() onderschept een panic (zeldzaam, op grenzen).
  • Nooit defer binnen een lange lus: deze stapelt zich op totdat de buitenste functie terugkeert.