ProgramlamaOrta/Kıdemli Go backend geliştirici

Go'da dilimlerde ve map'lerde for-range döngüleri nasıl çalışır ve kullanımında hangi değer kopyalama özellikleri ortaya çıkar?

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

Cevap.

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:

  • Her döngü iterasyonunda yeni key/value değişken kopyaları oluşur.
  • For-range içinde value'nun adresini almak, koleksiyondaki bir elemanın adresini değil, bir geçici değişkenin adresini döndürür.
  • Map için iterasyon rastgele bir sırada gerçekleşir, sıralama garantisi yoktur.

Kandırmaca Soruları.

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.

Tipik Hatalar ve Anti-Desenler

  • Farklı elemanlara referans saklamak için range içinde &value kullanımı.
  • İndekse erişmek yerine value değişkenini değiştirmek.
  • Map üzerinde sıralı geçiş beklentisi.

Hayattan Bir Örnek

Olumsuz Durum

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:

  • Döngünün kısalığı.

Eksiler:

  • Tüm referanslar aynıdır, bir elemanı değiştirdiğinizde "hepsi" değişir.

Olumlu Durum

İndeks üzerinde iterasyon yaparak gerekli elemanın göstericisini saklar:

for i := range arr { ptrs = append(ptrs, &arr[i]) }

Artılar:

  • Her gösterici, orijinal dilimin ayrı bir elemanına işaret eder.

Eksiler:

  • Söz dizimi daha uzun, ama beklenebilir bir sonuç.