In Go sind anonyme Funktionen (func literals) in der Lage, Closures zu erstellen, das heißt, sie können auf Variablen aus dem umgebenden Gültigkeitsbereich zugreifen, selbst nachdem dieser beendet wurde. Solche Closures belegen Speicher im Heap, wenn dies für eine korrekte Ausführung erforderlich ist (erkannt durch Escape-Analyse).
Beispiel:
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
Besonderheiten:
Was gibt dieser Code aus?
func main() { fs := []func(){} for i := 0; i < 3; i++ { fs = append(fs, func() { fmt.Println(i) }) } for _, f := range fs { f() } }
Viele werden sagen, dass 0, 1, 2 ausgegeben wird, jedoch wird das Ergebnis sein:
3
3
3
Alle Closures verweisen auf dieselbe Variable i; nach Abschluss der Schleife beträgt ihr Wert 3.
Richtig: eine Kopie der Variable im Schleifenrumpf erfassen:
for i := 0; i < 3; i++ { v := i // neue Variable fs = append(fs, func() { fmt.Println(v) }) }
Geschichte
Im Projekt für dynamisches Routing wurde eine Schleife verwendet, um viele Handler über Closures zu erstellen, jeder sollte seinen eigenen Pfad ergreifen. Infolgedessen druckten alle Handler den letzten Pfad — es wurde keine separate Variable in jedem Closure erstellt. Der Fehler wurde nur bei der Integration mit der HTTP-API entdeckt.
Geschichte
Beim Testen des parallelen Zugriffs über Goroutinen erfasste das Closure innerhalb der Schleife die Referenz auf den Index und nicht eine Kopie. Dies erzeugte "zufällige" Effekte: Daten wurden nicht in ihren eigenen Array-Slot geschrieben, sondern in den letzten.
Geschichte
In einer Funktion zur Statistiksammlung änderte das Closure eine gemeinsame Variable aus dem äußeren Bereich, obwohl der Autor einen unabhängigen Zähler für jede Aufgabe erwartete. Das Problem fiel aufgrund der unangemessen rekonstruierbaren Summe auf, die immer gemeinsam und nicht privat war, trotz lokaler Logik.