Go ile yıllardır kod yazıyorum ve dilin sadeliğini seviyorum. Ama tek bir konuda her seferinde içimden “bunun daha iyi bir yolu olmalı” demişimdir: anonim gömme (embedding) kullanan struct’ları başlatmak. İç içe geçmiş, tekrar tekrar tip adı yazdığımız o uzun literal’ler, Go’nun ilk gününden beri gözüme batan bir gürültüydü.
Go 1.27 ile bu gürültü sonunda bitiyor. keyed fields denilen küçük ama etkili bir sözdizimi değişikliği, terfi eden (promoted) alanları doğrudan kullanmamıza izin veriyor. Gelin neden bu kadar sevindiğimi örneklerle görelim.
Sorun: Neden Bu Kadar Çok Satır Yazıyorduk?
Go’da kompozisyon, kalıtımın (inheritance) yerini alır. Bir tipi başka bir struct’ın içine anonim olarak gömdüğümüzde, onun alanları dış struct’a “terfi eder” ve doğrudan p.CreatedAt gibi erişilebilir hale gelir. Bu, Clean Architecture yazımda bahsettiğim kompozisyon felsefesinin ta kendisidir.
Diyelim ki bir domain modelimiz var; ortak alanları küçük, odaklı tiplere ayırmışız:
type Timestamps struct {
CreatedAt time.Time
UpdatedAt time.Time
}
type Audit struct {
CreatedBy string
UpdatedBy string
}
type Product struct {
Name string
Price int
Timestamps // anonim gömme
Audit // anonim gömme
}
Erişimde her şey harika: product.CreatedAt ya da product.CreatedBy diyebiliyoruz, sanki hepsi tek bir düz struct’mış gibi. Ama iş bu struct’ı başlatmaya gelince Go 1.27’den önce şöyle bir manzarayla karşılaşıyorduk:
p := Product{
Name: "Mekanik Klavye",
Price: 1200,
Timestamps: Timestamps{
CreatedAt: now,
UpdatedAt: now,
},
Audit: Audit{
CreatedBy: "goktay",
UpdatedBy: "goktay",
},
}
Fark ettiniz mi? Erişirken düz olan yapı, başlatırken aniden iç içe geçiyor. Timestamps ve Audit adlarını tekrar yazmak, süslü parantezleri iç içe açmak zorundayız. Okurken kafamda hep şu soru belirirdi: “Madem alanlara düz erişiyorum, neden düz atayamıyorum?”
Go 1.27 Çözümü: Terfi Eden Alanlar Artık Anahtar Olabiliyor
Go 1.27, tam da bunu çözüyor. Terfi eden alanları, gömülü tipin adını hiç anmadan doğrudan literal içinde anahtar olarak yazabiliyoruz:
p := Product{
Name: "Mekanik Klavye",
Price: 1200,
CreatedAt: now, // Timestamps'ten terfi etti
UpdatedAt: now, // Timestamps'ten terfi etti
CreatedBy: "goktay", // Audit'ten terfi etti
UpdatedBy: "goktay", // Audit'ten terfi etti
}
İç içe süslü parantez yok, tekrar eden tip adı yok. Erişimdeki düzlük, artık başlatmada da geçerli. Bu, 12 satırlık gürültüyü 8 satırlık berrak bir yapıya indiriyor — üstelik hiçbir anlam kaybı olmadan.
Production Notu: Bu bir sözdizimi kolaylığı; altında yatan bellek düzeni (memory layout) veya performans hiç değişmiyor. Derleyici hangi alanın hangi gömülü tipe ait olduğunu zaten biliyor. Yani bu değişiklik “sıfır maliyetli” bir okunabilirlik kazanımıdır.
Gerçek Dünya: Konfigürasyon Struct’ları
Bu özelliğin gerçek değerini, günlük hayatta en çok gömme kullandığımız yerde görüyoruz: konfigürasyon. Yüksek trafikli bir serviste tipik bir ServerConfig, alt konfigürasyonları gömerek oluşturulur:
type DatabaseConfig struct {
Host string
Port int
Name string
MaxConns int
}
type RedisConfig struct {
Addr string
TTL int
}
type ServerConfig struct {
DatabaseConfig // anonim gömme
RedisConfig // anonim gömme
}
Eski usul, her gömülü tipi ayrı ayrı iç içe başlatmayı gerektiriyordu:
cfg := ServerConfig{
DatabaseConfig: DatabaseConfig{
Host: "localhost",
Port: 5432,
Name: "betco",
MaxConns: 100,
},
RedisConfig: RedisConfig{
Addr: "localhost:6379",
TTL: 300,
},
}
Go 1.27 ile aynı yapı, düz ve okunması kolay bir hale geliyor:
cfg := ServerConfig{
Host: "localhost", // DatabaseConfig
Port: 5432, // DatabaseConfig
Name: "betco", // DatabaseConfig
MaxConns: 100, // DatabaseConfig
Addr: "localhost:6379", // RedisConfig
TTL: 300, // RedisConfig
}
Mühendislik Notu: Bu tür domain ve config struct’larını sıfırdan elle yazmak yerine, JSON veya SQL şemanızdan otomatik üretmek isterseniz hazırladığım Golang Struct Generator aracını kullanabilirsiniz. Karmaşık bir API yanıtından struct çıkarmak için de Advanced JSON Formatter iş görür.
Asıl Güç: Çok Katmanlı Terfi
Özelliğin beni en çok etkileyen yanı, terfinin birden fazla katmanda çalışması. Bir tipi, başka bir gömülü tipin içinden geçerek en dışa terfi ettirebiliyorsunuz:
type Coordinates struct {
Lat float64
Lng float64
}
type Location struct {
Coordinates // anonim gömme
Altitude float64
}
type Warehouse struct {
Name string
Location // iki kat derinlikte anonim gömme
}
Go 1.26’da bir Warehouse başlatmak, üç kat iç içe süslü parantez demekti. Go 1.27’de ise Lat, Lng ve Altitude — hepsi iki katman üzerinden terfi ederek — doğrudan anahtar oluyor:
w := Warehouse{
Name: "Ana Depo",
Lat: 40.7128,
Lng: -74.0060,
Altitude: 10.5,
}
14 satırlık iç içe yapı, 6 satırlık düz bir bloğa iniyor. Derin gömme kullanan büyük yapılarda bu fark katlanarak büyüyor.
Ne Zaman Kullanmalı, Ne Zaman Kaçınmalı?
Her yeni özellik gibi bunu da körlemesine kullanmak yerine bilinçli tercih etmek gerekir.
Şu durumda sorunsuz çalışır: Terfi eden alan adı benzersiz olduğunda. Yani iki farklı gömülü tip aynı isimde bir alan terfi ettirmiyorsa.
Şu durumda derleyici sizi durdurur: Eğer iki gömülü tip de örneğin ID adında bir alan terfi ettiriyorsa, ID: yazdığınızda derleyici “hangisi?” diye hata verir. Bu durumda o alan için eski, açık iç içe sözdizimine geri dönersiniz. Bu aslında iyi bir şey — belirsizliği sessizce yutmak yerine derleme aşamasında yakalar.
Production Notu: Gömülü tip çok büyükse (örneğin 30 alanlı bir config) ve siz sadece 3 alanını dolduruyorsanız, bazen eski iç içe sözdizimi daha okunaklı olabilir; çünkü hangi alanın hangi gruba ait olduğunu görsel olarak belli eder. Kısacası: keyed fields’ı varsayılanınız yapın, ama gruplamayı vurgulamak istediğiniz karmaşık yapılarda iç içe sözdizimini bilinçli olarak koruyun.
Sonuç
Go 1.27’nin keyed fields özelliği, manşetlere çıkacak türden gösterişli bir yenilik değil. Ama tam da bu yüzden Go’nun ruhuna uygun: sessizce araya girip yıllardır katlandığımız bir sıkıntıyı ortadan kaldırıyor. Go’da struct gömme neredeyse her yerde olduğuna göre — domain modellerinden config’lere kadar — bu küçük değişikliğin faydasını daha ilk go build komutunda hissedeceksiniz.
Ben şimdiden yeni yazdığım her struct literal’ini bu sözdizimiyle yazmaya başladım bile. Dilin sizin için çalışması, sizin dil için çalışmanızdan her zaman iyidir.