Defer is een unieke mechanismen in Go die het mogelijk maakt om een functie uit te voeren na de voltooiing van de huidige functie, zelfs bij panics. Historisch gezien is dit vergelijkbaar met on-exit constructies, maar in Go is het geïmplementeerd als een stack van uitgestelde aanroepen. Een belangrijk detail is dat de parameters van de uitgestelde functie onmiddellijk worden berekend op het moment van het declareren van defer, niet op het moment van de daadwerkelijke aanroep!
Probleem — onduidelijk gedrag: men kan verwachten dat de parameters worden doorgegeven bij het activeren van defer, maar dat is niet zo. Dit leidt vaak tot bugs bij het werken met veranderlijke of externe variabelen.
Oplossing — altijd in gedachten houden dat de parameters onmiddellijk worden berekend, terwijl het effect van de uitgestelde aanroep later optreedt.
Voorbeeld code:
func f() { x := 5 defer fmt.Println(x) x = 10 } // Zal afdrukken: 5, en niet 10
Belangrijke kenmerken:
Wat zal de volgende code afdrukken? Waarom?
func main() { i := 0 defer fmt.Println(i) i = 1 }
Antwoord: zal 0 afdrukken. Het argument van de functie fmt.Println wordt onmiddellijk opgeslagen bij het declareren van defer.
Heeft het wijzigen van de variabele na de declaratie van defer invloed op de overdracht van de waarde naar de functie?
Nee, het heeft geen invloed — de berekening gebeurt bij de declaratie van defer:
defer fmt.Println(x) // Waarde van x wordt nu opgeslagen, niet later
Kun je een defer maken om de laatste toestand van de variabele te tonen?
Ja, met behulp van een anonieme functie (closure):
defer func() { fmt.Println(x) }() // zal de actuele waarde van x vastleggen op het moment van uitgestelde aanroep
De code wordt op de volgende manier aangeroepen:
var f *os.File // ... defer f.Close()
Maar f wordt later toegewezen, dus panic door aanroep van nil pointer!
Voordelen:
Nadelen:
Omhulling van een opruimactie via een anonieme uitgestelde functie met controle:
defer func() { if f != nil { f.Close() } }()
Voordelen:
Nadelen: