Ce face generics (parametri de tip)
Din Go 1.18, funcțiile și tipurile pot avea parametri de tip, scriși între paranteze drepte: func Suma[T Numar](valori []T) T. La apel, tipul este de obicei dedus din argumente (Suma([]int{1, 2})), dar poate fi dat și explicit (Suma[float64](x)).
Fiecare parametru de tip are o constrângere, adică o interfață care spune ce operații sunt permise: any (orice tip, dar fără operatori), comparable (permite ==, necesar pentru chei de map), cmp.Ordered (permite <) sau o listă de tipuri proprie, ca ~int | ~float64. Semnul ~ include și tipurile definite pe baza lor (de exemplu type Lei float64).
Pachetele slices, maps și cmp sunt scrise cu generics. Din Go 1.27, și metodele pot avea propriii parametri de tip: func (c Cos) Transforma[T any](f func(string) T) []T. Folosește generics când aceeași logică se repetă identic pentru mai multe tipuri; pentru comportamente diferite, interfețele obișnuite rămân mai potrivite.
Exemple
Exemplul 1: Funcție generică cu constrângere proprie
~float64 permite și tipul Lei, definit pe baza lui float64.
package main
import "fmt"
type Numar interface {
~int | ~int64 | ~float64
}
type Lei float64
func Suma[T Numar](valori []T) T {
var total T
for _, v := range valori {
total += v
}
return total
}
func main() {
fmt.Println(Suma([]int{9, 7, 10}))
fmt.Println(Suma([]float64{2.5, 3.25}))
fmt.Println(Suma([]Lei{19.99, 5.01}))
}
Rezultat
26
5.75
25
Exemplul 2: Tip generic: o stivă
package main
import "fmt"
type Stiva[T any] struct {
elemente []T
}
func (s *Stiva[T]) Pune(v T) { s.elemente = append(s.elemente, v) }
func (s *Stiva[T]) Scoate() (T, bool) {
var zero T
if len(s.elemente) == 0 {
return zero, false
}
ultim := s.elemente[len(s.elemente)-1]
s.elemente = s.elemente[:len(s.elemente)-1]
return ultim, true
}
func main() {
var pagini Stiva[string]
pagini.Pune("acasă")
pagini.Pune("produse")
pagini.Pune("coș")
for {
p, ok := pagini.Scoate()
if !ok {
break
}
fmt.Println("înapoi la:", p)
}
}
Rezultat
înapoi la: coș
înapoi la: produse
înapoi la: acasă
Exemplul 3: comparable și cmp.Ordered
Numara are nevoie de == (chei de map); Maxim are nevoie de >.
package main
import (
"cmp"
"fmt"
)
func Numara[T comparable](valori []T) map[T]int {
m := make(map[T]int)
for _, v := range valori {
m[v]++
}
return m
}
func Maxim[T cmp.Ordered](valori ...T) T {
m := valori[0]
for _, v := range valori[1:] {
if v > m {
m = v
}
}
return m
}
func main() {
fmt.Println(Numara([]string{"da", "nu", "da", "da"}))
fmt.Println(Maxim(3, 9, 4), Maxim("Arad", "Iași", "Cluj"))
}
Rezultat
map[da:3 nu:1]
9 Iași
Greșeli frecvente
- Operatori pe un parametru
any:invalid operation: operator + not defined on total (variable of type T constrained by any). Alege o constrângere care îi permite. Numarfără~: tipurile proprii (Lei) nu sunt acceptate (Lei does not satisfy Numar (possibly missing ~ for float64 in Numar)).- Metode cu parametri de tip proprii în Go mai vechi de 1.27: nu compilează (
syntax error: method must have no type parameters). Din Go 1.27 sunt permise.
Comenzi înrudite
any: Alias pentru interface{}: un tip care poate ține orice valoare; folosit și drept constrângere în generics.comparable: Constrângere predefinită pentru generics: tipurile care se pot compara cu == și != (pot fi chei de map).interface și interfețe: Interfața descrie un set de metode; orice tip care le are o implementează automat, fără declarație explicită.type: Definește un tip nou (pe baza altuia, struct, interfață, funcție) sau un alias pentru un tip existent.min și max: Returnează cel mai mic, respectiv cel mai mare dintre argumente; merg pe numere și texte (Go 1.21).