Go'da for döngü yapısı, bir başlatma bloğu (init), koşul kontrolü ve sonlandırıcı ifadenin yanı sıra içerebilir. Tarihsel olarak bu mekanizma, kod yazmayı kolaylaştırmak ve C benzeri dillerin alışkanlığı için oluşturulmuştur. Ancak Go'da döngü değişkeninin (i) kapsamı, yerleşik işlevler, kapamalar ve gorutinler içindeki davranışı güçlü bir şekilde etkileyen bir spesifikaya sahiptir.
Problem — Gorutinler veya kapamalar her döngü iterasyonunda çalıştırıldığında sıkça beklenmedik bir davranış ortaya çıkar: Değişken i kopyalanmaz, bunun yerine "başvuru üzerinden yakalanır", yani kapama döngünün genel değişkenine başvurur, bu da döngü sona erdikten sonra son değeri alır. Bu, tüm gorutinlerde/kapamalarda aynı sonuca yol açar, oysa mantık farklısını öngörebilir.
Çözüm — Her iterasyon değerinin aktarılması gerekiyorsa, değişkenin açık bir şekilde kopyalanmasını (ek bir değişken aracılığıyla) kullanılabilir veya kapamada argüman olarak geçirilebilir.
Kod örneği:
for i := 0; i < 3; i++ { go func(j int) { fmt.Println(j) }(i) // Doğru! Kopyalanmış değer } for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() // Hata: tüm gorutinler 3'ü yazdırır }
Anahtar özellikler:
Break veya continue kullanıldığında döngü değişkeninin kapsamı değişir mi?
Hayır. For'da ilan edilen değişkenin kapsamı daima bu döngü bloğuyla sınırlıdır. Break veya continue yalnızca mevcut iterasyonu keser, ama değişkeni dışarı aktaramaz.
For'un init kısmında ilan edilen bir değişken, döngü dışında bir yöntem içinde yakalanabilir mi?
Hayır. Değişken yalnızca for'un içinde ve ona dahil olan tüm bloklarda görünür, ama döngü tamamlandığında dışarda görünmez.
Eğer değişken yakalama, for'un içinde defer ifadesinde gerçekleşirse ne olur?
Aynı durum: defer fonksiyonu oluşturulduğu zamanda değil, defer çalıştırıldığı zamandaki mevcut değişken değerini "görecektir" (genellikle döngünün son değeri).
for i := 0; i < 3; i++ { defer fmt.Println(i) // tüm defer 3 yazdırır }
Go web sunucusunda geliştirici, döngü değişkeni olarak bağlantı noktasını kullanarak çeşitli bağlantı noktalarını işlemek için birkaç gorutin başlattı ve bunu doğrudan lambda ifadesinde yakaladı. Tüm gorutinler, dizideki son bağlantı noktasına erişti.
Artılar:
Eksiler:
Takım, her zaman döngü değişkeninin değerini yeni bir değişkene kopyalama kuralı getirdi, kapama/gorutin tarafından yakalanacak olan.
Artılar:
Eksiler: