ProgramlamaBackend Go Geliştiricisi

Go'da iç içe fonksiyonlar ve döngülerde değişkenlerin adlandırılması ve kapsamı nasıl çalıştığını anlatın. Hangi tuzaklara dikkat etmek gerekir?

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

Cevap.

Go'da değişkenlerin kapsam kuralları (scoping) bloklar ({}) ile katı bir şekilde belirlenir ve bir değişkenin adı iç içe alanlarda gölgelenebilir (shadowing). Özellikle iç içe fonksiyonlar, anonim fonksiyonlar, döngüler ve farklı seviyelerde aynı isimle değişkenler tanımlandığında birçok tuzak ortaya çıkar.

Sorunun Tarihçesi

Go, kodun okunabilirliğini artırmak için kapsam ile ilgili "büyülü" davranışları en aza indirmiştir. Ancak, sentez esnekliği ve kısa biçimle değişkenlerin yeniden tanımlanmasına izin verilmesi, algıda hatalara yol açmaktadır.

Problem

Eğer bir iç içe fonksiyonda veya bir döngü bloğunda üst seviyedekiyle aynı isme sahip bir değişken tanımlanırsa, dıştaki değişken erişilemez (gölgelendi — shadowed) olur. Çoğu durumda bu, derleyici tarafından fark edilmez ve özellikle closure ile çalışırken hatalara neden olabilir. Bir diğer yaygın hata, bir if bloğunda veya for-init içinde yeni bir değişken tanımlamak ve ardından bu değişkene blok dışında erişmeye çalışmaktır.

Çözüm

Her zaman kapsam seviyelerine dikkat edin. İç içe bloklarda veya anonim fonksiyonlarda gerçek bir ihtiyaç olmadan aynı değişken isimlerini kullanmaktan kaçının, kısa isimler kullanmayın ve := kullanırken dikkatli olun.

Kod örneği:

package main import "fmt" func main() { x := 1 { x := 2 // main()'den x'i gölgeler fmt.Println("İç x:", x) } fmt.Println("Dış x:", x) for i := 0; i < 3; i++ { x := i // her iterasyonda yeni bir x oluşturulur go func() { fmt.Println("Goroutine x:", x) }() } }

Bu örnekte dıştaki değişken x değişmezken, iç blokta yeni bir x oluşturulmaktadır. İkinci döngüde x değişkeni içteki fonksiyon tarafından yakalanır — sonuç beklenmedik olabilir.

Anahtar özellikler:

  • Her blok, üst seviyedeki değişkenleri gölgeler;
  • Aynı isimle iki değişken tanımı, farklı alanlarda bulunduğunda birbirleriyle ilişkili değildir;
  • Closure bir değişkeni, o değişkenin döngü iterasyonu anındaki değerini değil, referansını yakalar;
  • Blok içinde := kısa biçimi her zaman yeni bir değişken oluşturur, dışarıda bir değişken olsa bile.

Kandırmaca Soruları.

1. Gölgeleme durumunda iç blokta son olarak hangi değişkenin değeri yazdırılacak?

Dıştaki değişkenin değeri, çünkü içteki değişken sadece blok içinde var olur.

2. Eğer bir blok içinde tanımlanan bir değişkene, o bloğun dışında erişmeye çalışırsanız ne olur?

Derleyici hata verecektir: değişken kapsam dışındadır.

if true { y := 5 } fmt.Println(y) // hata

3. Değişkeni döngüde goroutine oluştururken beklenmedik bir değer almaktan nasıl kaçınılır?

Değişkeni fonksiyonun parametresi olarak geçirerek:

for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }

Tipik Hatalar ve Anti-Patternler

  • Farklı seviyelerde aynı kimlik kullanmak — veriler kaybolur, izlemek zorlaşır;
  • Goroutine'lerde değişkenleri yakalamak, onları belirgin bir şekilde argüman olarak geçmeden;
  • Kısa biçim :='in mevcut bir değişkeni değiştireceğini beklemek (yeni bir değişken oluşturur).

Gerçek Hayat Örneği

Negatif Durum

Döngü, paralel işleme için birkaç goroutine başlatır, ancak closure içinde döngü değişkeni kullanılmadan geçilmektedir — tüm goroutine'ler onun "son" değeriyle çalışmaktadır.

Artıları:

  • Özlü, az kod.

Eksileri:

  • Tahmin edilemeyen davranış, prod'da hatalar, veriler kaybolur.

Pozitif Durum

Döngü değişkeninin closure parametresi olarak geçirilmesi — her goroutine kendi değerini alır.

Artıları:

  • Doğru çalışma, veri yarışı ve beklenmedik durumlar yok.

Eksileri:

  • Fonksiyon parametrelerinin listesini açıkça tanımlamak gerekir.