In Go sind Closures Funktionen, die Variablen aus ihrer umgebenden Sichtbarkeit "einfangen". Am häufigsten werden Closures für anonyme Funktionen verwendet, die innerhalb anderer Funktionen erstellt werden.
Das häufigste Problem bei der Arbeit mit Closures ist das unerwartete Verhalten beim Einsatz von Schleifenvariablen innerhalb einer Goroutine:
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }
Jede Goroutine kann denselben Wert von i ausgeben, weil die Variable i läuft und das Closure die Variable selbst und nicht ihren Wert in jeder Iteration einfängt.
Richtiger Weg:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
Dieses Verhalten ist darauf zurückzuführen, dass das Closure einen Verweis auf die Variable (ihre Adresse) und nicht ihren geschnittenen (by value) Wert hält.
Welchen Wert geben mehrere gestartete Goroutinen innerhalb einer Schleife aus, wenn sie die Schleifenvariable einfangen?
Antwort: Alle Goroutinen können denselben Wert ausgeben (häufig den letzten), da sie den aktuellen Wert der Variablen sehen und zu dem Zeitpunkt, an dem die Goroutine ausgeführt wird, die Schleife bereits abgeschlossen ist. Um dies zu vermeiden, muss die Variable als Parameter an das Closure übergeben werden.
Beispiel:
for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // höchstwahrscheinlich erhalten wir: 5 5 5 5 5
Geschichte
Geschichte
Geschichte
In einem Kurier-Startup führte die fehlerhafte Verwendung von Closures beim Aktualisieren der Bestellkoordinaten dazu, dass massenhaft die Koordinaten der letzten Bestellung im Slice aktualisiert wurden, anstatt der aktuellen – aufgrund eines Rennens beim Zugriff auf das Slice innerhalb der anonymen Funktion.