Sorunun Tarihi:
For-range yapısı, Go'da koleksiyonlar (dilimler, diziler, map'ler, stringler) üzerinde yineleme için bir yöntem olarak ortaya çıktı. Go geliştiricileri, döngünün her iterasyonunda değerin bir kopyasını almayı ve doğrudan referans olarak kullanmamayı sağlamak için bir optimizasyon uyguladı ve bu da özellikle döngü değişkenleri ile beklenmedik hatalara yol açabilir.
Sorun:
Birçok kişi range içindeki bir değişkenin adresini almaya çalışırken (örneğin, &v), koleksiyondaki bir elemanın adresini aldıklarını düşünerek hata yapar; aslında yerel bir değişkenin adresini alıyorlar.
Çözüm:
For-range döngüsünde, her iterasyonda iterator değişkenlerinin (key, value) yeni kopyaları oluşturulur. Basit türler için bu sorun olmazken, yapılar için elemanın göstericisini saklandığında beklenmedik sonuçlara yol açar — her zaman aynı değişkene referans verecektir, farklı dilim elemanlarına değil.
Kod örneği:
people := []Person{{Name: "Ivan"}, {Name: "Oleg"}} ptrs := make([]*Person, 0) for _, p := range people { ptrs = append(ptrs, &p) // tüm ptrs aynı p'ye referans verecek }
Anahtar özellikler:
Range içinde value değişkenine referans saklarsak ne olur?
Tüm referanslar aynı belleği gösterecek, çünkü value geçici bir değişken.
for _, v := range someSlice { ptrs = append(ptrs, &v) } // Tüm ptrs aynı değişkene referans içerir!
Range'de value üzerinden referansla koleksiyon elemanını değiştirebilir miyiz?
Hayır, value'nun değiştirilmesi koleksiyonun orijinal elemanını etkilemez. Değişiklik yapmak için indekse erişmek gereklidir.
for _, v := range arr { v.Field = 10 // arr değişmeyecek } for i := range arr { arr[i].Field = 10 // doğru }
For-range, map üzerinde sıralı bir geçiş garantiler mi?
Hayır, Go'da map üzerindeki iterasyon sırası belirli değildir ve her uygulama çalıştırışında farklı olabilir.
Geliştirici, range üzerinden yapı elemanlarının adreslerini seri hale getirmeye çalışırken &value'yi ayrı bir dilime kaydetmeye çalışır. Sonuçta benzer adreslerden oluşan bir dilim elde eder.
Artılar:
Eksiler:
İndeks üzerinde iterasyon yaparak gerekli elemanın göstericisini saklar:
for i := range arr { ptrs = append(ptrs, &arr[i]) }
Artılar:
Eksiler: