ProgramlamaKıdemli Go Geliştirici

Go, kapalı işlemler (func literals/closures) nasıl uygulanır ve kapalı işlemlerin kullanılmasındaki kısıtlamalar ve özellikler nelerdir: nerede depolanır, değişkenler nasıl yakalanır, farklı senaryolarla yakalanan değişkenlerin davranışı neyle farklıdır?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap

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:

  • Kapalı işlem, dış görünüm alanındaki değişkenleri referansla (o anki değerleri değil) yakalar.
  • Eğer değişken dışarıda değiştirilirse, kapalı işlem yeni değeri görür.
  • Kapalı işlem bir fonksiyondan dönerse, yakalanan değişkenler kapalı işlemin ömrü sona erene kadar var olacaktır.
  • Eğer kapalı işlem kullanılmazsa, escape analizi değişkenlerin heap’e gitmemesine izin verebilir.

Kandırıcı Soru

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) }) }

Gerçek hata örnekleri


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.