Menu

Golang Generics: Tip Parametreleri, Kısıtlar ve Örnekler

Generics (Go 1.18+), tek bir fonksiyonun ya da tipin tip güvenliğini koruyarak birçok tiple çalışmasını sağlar. Tip parametrelerini, any, comparable ve cmp.Ordered kısıtlarını, ~ ile özel kısıtları, generic tipleri ve ne zaman kullanılmaması gerektiğini öğrenin.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Generic bir fonksiyon

Tip parametreleri normal parametrelerden önce köşeli parantez içine yazılır. Her birinin hangi tiplere izin verildiğini söyleyen bir kısıtı vardır.

T ve U argümanlardan çıkarılır, bu yüzden nadiren Map[int, string](...) yazarsınız. Çıkarım fonksiyon argümanlarından çalışır; yalnızca sonuçta görünen bir tipi çıkaramaz. func Zero[T any]() T için Zero[int]() yazmanız gerekir.

Go 1.18'den önce bu fonksiyonun her tip için bir kez yazılması ya da []interface{} alıp tip güvenliğini kaybetmesi gerekiyordu.

Kısıtlar

Kısıt bir interface'tir. Hangi tiplerin kullanılabileceğini sınırlar ve karşılığında derleyiciye o tipteki değerler üzerinde hangi işlemlere izin verildiğini söyler.

Kısıtİzin verdiğiKullanmanıza izin verdiği
anyher tipatama, aktarma, saklama
comparable== destekleyen tipler==, !=, map anahtarları
cmp.Ordered (Go 1.21)tam sayılar, float'lar, string'ler<, <=, >, >=, ayrıca ==
metotları olan bir interfaceo metotlara sahip tiplermetotları çağırmak
~int | ~float64 gibi bir tip birleşimitam olarak o tiplerhepsinin desteklediği operatörler

comparable yerine any ile v == target derlenmez: invalid operation: v == target (incomparable types in type set). Operatörü geçerli kılan kısıttır.

Bu iki yardımcı zaten var: slices.Index ve slices.Max. Standart slices, maps ve cmp paketleri gündelik generic ihtiyaçların çoğunu karşılar, bu yüzden kendinizinkini yazmadan önce onlara bakın.

Tip birleşimleri ve ~ ile özel kısıtlar

Bir kısıt tipleri | ile listeleyebilir. İzin verilen operatörler, listelenen her tipin desteklediği operatörlerdir.

Tilde önemlidir. ~int64, "temel tipi int64 olan herhangi bir tip" demektir, bu yüzden adlandırılmış Cents tipi kabul edilir. Düz int64 olarak yazılsaydı Sum([]Cents{...}), Cents does not satisfy Number (possibly missing ~ for int64 in Number) hatasıyla başarısız olurdu.

Tip birleşimi içeren interface'ler yalnızca kısıt olarak kullanılabilir, asla sıradan değişken tipi olarak kullanılamaz. var n Number derlenmez.

golang.org/x/exp/constraints paketinde hazır Integer, Float ve Signed kısıtları vardır. Standart kütüphanenin dışındadır, bu yüzden buradaki çalıştırıcı onu import edemez; standart olan cmp.Ordered'dır.

Generic tipler

Struct'lar, slice'lar, map'ler ve diğer tiplerin de tip parametreleri olabilir. Generic bir tipteki metotlar alıcının parametrelerini kullanır.

Bu örnekteki iki ayrıntıyla sürekli karşılaşırsınız:

  • T'nin sıfır değeri. Generic koddan "hiçbir şey" döndürmenin yolu var zero T'dir. Her tip için çalışan bir T{} ya da nil yoktur.
  • Örnekleme (instantiation). Generic bir tip kullanılmadan önce örneklenmelidir: Stack değil, Stack[string]. Kendi metotlarının içinde alıcı Stack[T] olarak yazılır.

Go 1.24 generic tip takma adlarını ekledi: type Set[T comparable] = map[T]struct{} artık derleniyor.

Karşılaşacağınız sınırlar

  • Metotlarda tip parametresi yok. func (s *Stack[T]) Map[U any](...) izinli değildir. Üst seviye bir fonksiyon kullanın.
  • Özelleştirme (specialization) yok. T = string için ayrı bir gerçekleştirme yazamazsınız. Fonksiyonun içinde any(v) üzerinde bir type switch geçici çözümdür ve genellikle generics'in yanlış araç olduğunun bir ipucudur.
  • Ada göre operatör kısıtı yok. "+ metodu olan herhangi bir tip" diyemezsiniz; tipleri bir birleşimde listelersiniz.
  • Bir kısıt üzerinden alan erişimi çalışmaz. Bir birleşimdeki her tipin ID alanı olsa bile v.ID'ye izin verilmez. Bunun yerine kısıtta bir metot kullanın.

Generics ne zaman kullanılmamalı

Generics, container tiplerine (stack'ler, set'ler, önbellekler, ağaçlar) ve her eleman tipi için aynı şeyi yapan slice ve map algoritmalarına uyar. Her yere uymaz.

  • Tek bir somut tip. Fonksiyon her zaman yalnızca []User ile çağrılıyorsa, generic bir sürüm okuma maliyetinden başka hiçbir şey eklemez.
  • Tipe göre değişen davranış. Interface'ler bunun içindir. Bir io.Writer alan fonksiyon, [T io.Writer] alandan daha basittir ve aynı şekilde çalışır.
  • Interface parametrelerinin yerini almak. func Print[T fmt.Stringer](v T), func Print(v fmt.Stringer)'dan daha iyi değildir.

Go ekibinin kendi ilkesi: önce kodu belirli bir tip için yazın ve aynı kodu yalnızca tipleri değiştirerek ikinci kez yazdığınızı fark ettiğinizde tip parametrelerine başvurun.

Sık yapılan hatalar

  • comparable ya da cmp.Ordered gereken yerde any kullanmak. Derleyici any bir tip parametresi üzerinde == ya da <'ı reddeder.
  • ~'yi unutmak. type ID int gibi kullanıcı tanımlı tipler düz bir int kısıtını karşılamaz.
  • Bir tip parametresi için nil döndürmek. Kısıt T'yi pointer benzeri tiplerle sınırlamadıkça izinli değildir. Sıfır değer döndürün.
  • Zaten var olan generic bir yardımcıyı yazmak. Önce slices, maps ve cmp'ye bakın.

Sıkça Sorulan Sorular

Go'da generics var mı?

Evet, Go 1.18'den (Mart 2022) beri. Fonksiyonlar ve tipler köşeli parantez içinde tip parametreleri tanımlayabilir: func Map[T, U any](s []T, f func(T) U) []U. Her tip parametresinin bir kısıtı vardır; bu kısıt, tipin neyi desteklemesi gerektiğini tanımlayan bir interface'tir.

Go generics'te any ile comparable arasındaki fark nedir?

any her tipe izin verir, ama değerlerle atamak, aktarmak ve saklamak dışında neredeyse hiçbir şey yapmanıza izin vermez. comparable yalnızca == ve != destekleyen tiplere izin verir; map anahtarları ya da eşitlik kontrolleri için ihtiyacınız olan budur. < ve > için cmp.Ordered kullanın.

Bir Go kısıtında tilde (~) ne anlama gelir?

~T, "temel tipi T olan herhangi bir tip" demektir. ~int hem int ile hem de type Celsius int ile eşleşir. Tilde olmadan bir kısıttaki int yalnızca int'in kendisiyle eşleşir, bu yüzden kullanıcı tanımlı tipler reddedilir.

Go metotlarının tip parametreleri olabilir mi?

Hayır. Bir metot alıcı tipinin tip parametrelerini kullanabilir (func (s *Stack[T]) Push(v T)), ama yenilerini tanımlayamaz. Bunun yerine üst seviye bir generic fonksiyon yazın: func Map[T, U any](s *Stack[T], f func(T) U) *Stack[U].

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA