In Go zijn closures functies die variabelen uit hun omliggende scope "vastleggen" (capturing). Het meest gebruikelijke gebruik van closures is voor anonieme functies die binnen andere functies worden gemaakt.
De meest typische probleem bij het werken met closures is onverwacht gedrag bij het gebruik van loopvariabelen binnen een goroutine:
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }
Elke goroutine kan dezelfde waarde van i afdrukken, omdat de variabele i blijft loop-en het closure legt de variabele vast, niet de waarde ervan bij elke iteratie.
De juiste manier:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
Dit gedrag komt doordat het closure een referentie naar de variabele (de adres) vasthoudt, en niet haar gesneden (by value) waarde.
Welke waarde zullen meerdere gestart goroutines binnen een loop afdrukken als ze de loopvariabele vastleggen?
Antwoord: Alle goroutines kunnen dezelfde waarde afdrukken (vaak de laatste), omdat ze de huidige waarde van de variabele zien, en tegen de tijd dat de goroutine uitgevoerd wordt, de loop al is beëindigd. Om dit te vermijden, moet de variabele als parameter aan het closure worden doorgegeven.
Voorbeeld:
for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // waarschijnlijk krijgen we: 5 5 5 5 5
Verhaal
Verhaal
Verhaal
In een koeriersstartup leidde onjuist gebruik van closures bij het bijwerken van de coördinaten van een bestelling ertoe dat massaal de coördinaten van de laatste bestelling in de slice werden bijgewerkt, en niet de huidige — door een race bij toegang tot de slice binnen de anonieme functie.