Go'da anonim fonksiyonlar (func literals) kapalı işlemler oluşturarak, etrafındaki görünüm alanındaki değişkenlere erişim sağlar; bu, görünüm alanı tamamlandıktan sonra bile mümkündür. Böyle kapalı işlemler, doğru çalışmaları için gereken durumlarda heap'te bellek ayırır (escape analizi ile belirlenir).
Örnek:
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
Özellikler:
Bu kod ne verecektir?
func main() { fs := []func(){} for i := 0; i < 3; i++ { fs = append(fs, func() { fmt.Println(i) }) } for _, f := range fs { f() } }
Birçok kişi 0, 1, 2 yazdırır diye cevap verecektir, ancak sonuç:
3
3
3
Tüm kapalı işlemler aynı i değişkenine atıfta bulunur; döngü tamamlandığında bu değişkenin değeri 3'tür.
Doğru: döngü gövdesinde değişkenin kopyasını yakalamak:
for i := 0; i < 3; i++ { v := i // yeni değişken fs = append(fs, func() { fmt.Println(v) }) }
Hikaye
Dinamik yönlendirme projesinde, her bir handler'ın kendi yolunu yakalaması için kapalı işlemler kullanarak bir döngü oluşturuldu. Sonuçta, tüm handler'lar son yolu yazdırdı — her kapalı işlemin içinde ayrı bir değişken oluşturulmadı. Hata, HTTP API entegrasyonu sırasında ortaya çıktı.
Hikaye
Döngü içinde goroutine'ler aracılığıyla eşzamanlı erişimi test ederken, kapalı işlem indeksi referansla, değil kopyayla yakaladı. Bu "rastgele" etkilere neden oldu: veriler kendi dizi slotlarına değil, sonuncusuna yazıldı.
Hikaye
İstatistik toplama fonksiyonunda kapalı işlem, dış görünüm alanındaki ortak bir değişkeni değiştirdi; oysa yazar, her görev için bağımsız bir sayıcı bekliyordu. Problem, her zaman ortak olmaya devam eden, parçalı değil tamamen toplu, yetersiz bir toplamın görünmesi sonucu fark edildi.