Go'da yapılar (struct) varsayılan olarak değer olarak geçilir ve döner. Bu, bir fonksiyon çağrıldığında veya ondan dönüş yapıldığında yapının tamamının kopyalanacağı anlamına gelir. Küçük yapılar için bu şeffaf olabilir, ancak büyük yapılar için bu bir sorun teşkil eder.
Başlangıçta Go, az sayıda tahsisat ile verimli bir çalışma sağlamayı amaçlıyordu. Ancak, birden fazla alan ve iç içe geçmiş nesneler kullanan yapılar büyük verilerin yanlış kopyalanma tehlikesini ortaya çıkardı. Bu tür işlemlerin performansı zarar görebilir ve bazen fark eğilimli yalnızca profil kullanımında veya GC (Çöp Toplayıcı) ile ilgili sorunlarda ortaya çıkar.
Eğer bir yapı büyük bir boyuta sahipse, her fonksiyon çağrısında, dönüşte veya atamada kopyalanması maliyetli hale gelir. Bu da:
Büyük yapılar için yapı nesnesini değil, yapının işaretçisini (*T) geçmek ve döndürmek önerilir. Bu, maliyetleri düşürür ve tek bir veri örneği ile çalışmayı sağlar.
Kod örneği:
package main import "fmt" type Large struct { Data [1024]int } // Değerle geçiş (büyük nesneler için hatalı) func ValueProcess(l Large) { l.Data[0] = 123 // yalnızca kopyayı değiştirir } // İşaretçi ile geçiş func PointerProcess(l *Large) { l.Data[0] = 456 // orijinal veriyi değiştirir } func main() { a := Large{} ValueProcess(a) fmt.Println("ValueProcess'ten sonra:", a.Data[0]) // 0 PointerProcess(&a) fmt.Println("PointerProcess'ten sonra:", a.Data[0]) // 456 }
Anahtar özellikler:
1. Go'da bir fonksiyondan yerel bir yapı değişkeninin işaretçisini döndürmek mümkün mü?
Evet. Go, döndürülen işaretçilerin geçerli olacağını garanti eder, otomatik olarak işaretçinin referans verdiği değerleri yığında taşır (escape to heap).
func NewLarge() *Large { l := Large{} return &l }
2. Eğer fonksiyona yapı değer olarak geçersek ve içindeki alanları değiştirirsek, orijinal değişir mi?
Hayır: yalnızca kopya değişir, ve dışarıdaki orijinal değişmeden kalır.
3. Yapılar için her zaman işaretçi kullanmalı mıyız?
Hayır. Küçük (birkaç alan içeren) yapılar için değerle geçiş güvenlidir ve genellikle öncelikli (immutable/value-semantic) olarak tercih edilir, tahsisatları azaltarak ve GC üzerindeki yükü düşürerek.
Bir kayıt hizmetinde, her olay büyük bir yapıyı temsil ediyordu ve işlevlerden değerle geri dönüyordu — her değişiklik yapı tamamı kopyalanıyordu.
Artıları:
Eksileri:
Yapıların işaretçi ile geçirilmesi ve döndürülmesine geçildi, veriler func(l *Large) ve func() *Large türlerindeki imzalarla değiştirildi.
Artıları:
Eksileri: