In Go kunnen anonieme functies (func literals) closures creëren, wat betekent dat ze toegang hebben tot variabelen uit de omringende scope, zelfs nadat deze is voltooid. Dergelijke closures alloceren geheugen in de heap als dit nodig is voor een correcte werking (gedetecteerd via escape-analyse).
Voorbeeld:
func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } a := adder() printf("%d\n", a(5)) // 5 printf("%d\n", a(10)) // 15
Kenmerken:
Wat zal deze code afdrukken?
func main() { fs := []func(){} for i := 0; i < 3; i++ { fs = append(fs, func() { fmt.Println(i) }) } for _, f := range fs { f() } }
Veel mensen zullen antwoorden dat het 0, 1, 2 zal afdrukken, echter zal het resultaat zijn:
3
3
3
Alle closures verwijzen naar dezelfde variabele i; na afloop van de lus is de waarde 3.
Correct: een kopie van de variabele vastleggen in de lus:
for i := 0; i < 3; i++ { v := i // nieuwe variabele fs = append(fs, func() { fmt.Println(v) }) }
Verhaal
In een dynamisch routeringsproject werd een lus gebruikt om meerdere handlers te creëren via closures, die elk hun eigen pad moesten vastleggen. Als gevolg hiervan drukten alle handlers het laatste pad af — er was geen aparte variabele in elke closure gemaakt. De fout werd alleen ontdekt tijdens de integratie met de HTTP API.
Verhaal
Bij het testen van gelijktijdige toegang via goroutines binnen de lus, ving de closure een referentie op de index, niet een kopie. Dit veroorzaakte "willekeurige" effecten: gegevens werden niet in hun eigen array-slot geschreven, maar in het laatste.
Verhaal
In de functie voor het verzamelen van statistieken wijzigde de closure een gemeenschappelijke variabele uit de externe scope, terwijl de auteur een onafhankelijke teller voor elke taak verwachtte. Het probleem werd opgemerkt aan de inconsistent reproduceerbare som, die altijd gemeenschappelijk was, niet privé, ondanks de lokale logica.