defer stelt de uitvoering van een functie uit tot het verlaten van de omringende functie — zelfs als het verlaten plaatsvindt door paniek of return. Wanneer defer binnen een lus wordt gebruikt, worden alle uitgestelde aanroepen opgestapeld in de defer-aanroepstack en worden ze in omgekeerde volgorde uitgevoerd bij het beëindigen van de omringende functie.
Dit kan leiden tot onverwacht resourceverbruik en vertragingen, aangezien alle uitgestelde functies gelijktijdig worden aangeroepen pas na het verlaten van de functielichaam, en niet na elke iteratie van de lus.
Voorbeeldcode:
func readFiles(files []string) { for _, name := range files { f, _ := os.Open(name) defer f.Close() // bronnen worden pas vrijgegeven na de voltooiing van de gehele functie // verwerking van het bestand ... } }
In dit voorbeeld blijven de bestanden open tot het einde van de functie, wat kan leiden tot een lek van descriptors bij een groot aantal bestanden.
Wat gebeurt er als je defer gebruikt om middelen binnen een lus te sluiten? Waarom is dit niet altijd optimaal?
Antwoord: Alle defer-aanroepen worden opgestapeld en worden pas geactiveerd na de voltooiing van de functie, niet na elke iteratie. Dit betekent dat middelen (zoals geopende bestanden) te laat zullen worden vrijgegeven.
Juist:
for _, name := range files { f, _ := os.Open(name) // defer f.Close() // niet doen! // Juist: process(f) f.Close() }
Verhaal
In een logboekbeheerproject deed zich een probleem voor: de dienst stopte plotseling met het openen van nieuwe bestanden, terwijl er maar weinig waren. De reden was defer in de lus. Alle bestanden werden geopend, terwijl hun sluiting tot het einde van de functie werd uitgesteld. Na herschrijven naar een expliciete Close() na verwerking verdween het probleem.
Verhaal
In een dienst die metrics verzamelt voor grote lijsten, werd defer gebruikt voor het resetten van de databaseverbinding binnen de lus van datagegevens. Met het toenemen van het aantal iteraties traden vertragingen op en werd de drempel van geopende verbindingen overschreden, waardoor de dienst begon te falen door fouten "too many open connections".
Verhaal
Een engineer, in de hoop op "elegante" opruiming, paste defer toe in een lus die een groot aantal bestanden leest, wat leidde tot het overschrijden van de limiet voor geopende descriptors op de server in productie en tot een stopzetting van de dienst.