ProgramlamaBackend Geliştirici

Go'da genel hikayeler (generics) nasıl uygulanmıştır? Hangi kısıtlamalar, sözdizimi, gizli tuzaklar ve uygun kullanım durumları vardır?

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

Cevap.

Genel hikayeler veya generics, Go 1.18 sürümünden itibaren ortaya çıkmıştır. Uzun bir süre Go, basitlik uğruna genel hikayeleri dışlayan muhafazakar bir dil olarak kabul edilmiştir, ancak proje sayısının artması ve ekosisteminin gelişmesi, evrensel fonksiyonlar ve yapılar yazma ihtiyacını doğurmuştur. Bu, özellikle konteyner yapıları, koleksiyon işleme algoritmaları ve altyapı kodları için kritik bir önem taşımaktadır.

Sorun: Genel hikayelerden önce, kodu çoğaltmak veya boş arayüzler (interface{}) kullanmak zorundaydık, bu da tip güvenliğinin kaybına, performans düşüklüğüne ve hata ayıklamanın zorlaşmasına yol açıyordu.

Çözüm: Genel hikayeler, Go'da fonksiyonlar ve türler için köşeli parantezler içinde belirtilen tür parametreleri aracılığıyla uygulanmaktadır. Kısıtlamalar (constraints) ile geçerli tür parametrelerini sınırlamak mümkündür. Bu, tip güvenliği kaybedilmeden genel fonksiyonlar yazmayı sağlar.

Kod örneği:

package main import "fmt" type Adder[T any] func(a, b T) T func Sum[T any](slice []T, add Adder[T]) T { var result T for _, v := range slice { result = add(result, v) } return result } func intAdder(a, b int) int { return a + b } func main() { nums := []int{1, 2, 3, 4} sum := Sum(nums, intAdder) fmt.Println(sum) }

Anahtar özellikler:

  • Tip güvenliği — derleme sırasında tip hatalarının önlenmesi.
  • Kısıtlamalar (constraints) — genel hikayelerin yalnızca belirli arayüzleri gerçekleştiren veya karşılaştırma yeteneğine sahip türlerle sınırlı olmasını sağlama imkanı.
  • Uyumluluk — yeni geliştiriciler, projeleri hemen yeniden yapılandırmadan ihtiyaç duydukça genel hikayeleri kullanabilir.

Soru Tuzağı.

Genel hikayelerdeki T tür parametreleri için herhangi bir aritmetik işlem (+, -, *, /) kullanılabilir mi?

Hayır. Go derleyicisi, tür parametresinin aritmetiği destekleyip desteklemediğini bilmez. Bunun için, Go 1.18+ ile kısıtlama, örneğin, işleci kısıtlamaları içeren bir arayüz belirtmek gerekir.

Kod örneği:

type Addable interface { int | float64 | uint } func Sum[T Addable](slice []T) T { var result T for _, v := range slice { result += v } return result }

Generic konteynerler, farklı türler için farklı davranışlarla yöntemler içerebilir mi?

Hayır. Genel türlerin veya fonksiyonların yöntemleri, tür değişimi kullanılmadığı sürece tüm türler için aynıdır. Davranış, kısıtlamalar aracılığıyla tanımlanmalı ya da tamamen parametreize edilmelidir.

Paket düzeyinde tür parametreleri (generic types) oluşturmak mümkün mü, yoksa yalnızca fonksiyonlar için mi?

Evet, Go 1.18'den itibaren hem genel fonksiyonlar hem de genel yapı türleri oluşturmak mümkündür:

type Stack[T any] struct { items []T } func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }

Tür Hataları ve Antipatternlar

  • Yeni Go sürümlerinde generics yerine interface{} kullanımı.
  • Kısıtlamaların (constraints) belirtilmemesi, evrensel kod içinde işlemlerin kullanılamaması.
  • Basit fonksiyonların aşırı genelleştirilmesi (genelleme amaçlı generics).

Gerçek Hayattan Örnek

Olumsuz Durum

Koleksiyonlarla çalışmak için bir kütüphanede, interface{} aracılığıyla evrensel bir Map işlevselliği uygulandı:

Artılar:

  • Evrensel, herhangi bir tür için kullanılabilir.

Eksiler:

  • Tip güvenliğinin olmaması, elle tip dönüştürme gereksinimi, çalışma zamanı hataları.

Olumlu Durum

Aynı projede generics'e geçildi ve arayüzler aracılığıyla kısıtlamalar belirlendi:

Artılar:

  • Tip güvenliği, hatalar derleme sırasında tespit edilir, destekleme kolaylaşır.

Eksiler:

  • Yeni sözdizimini öğrenmek gerekir, bazı IDE'ler karmaşık kısıtlamaları kötü destekler.