Salt la conținutul principal
Modulul 10 · Lecția 3 din 548/50 în curs~15 min
Lecții din modul (3/5)

Generic (Go 1.18+)

Începând cu Go 1.18 (martie 2022), parametrii de tip sunt disponibili: funcțiile și tipurile pot fi parametrizate peste tipuri. Vă permit să scrieți Map, Filter, Set[T], LinkedList[T] fără a duplica cod și fără interface{} + aserțiuni de tip runtime.

Funcția generică: sintaxa

Go
func Map[T, U any](s []T, f func(T) U) []U {
    out := make([]U, len(s))
    for i, v := range s {
        out[i] = f(v)
    }
    return out
}

doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 })
// doubled is []int{2, 4, 6}

Trei elemente noi:

  • [T, U any] după nume: declară parametrii de tip cu constrângerile lor.
  • any = constrângere care acceptă orice tip (alias de interface{} începând cu Go 1.18).
  • Inferență: apelând Map([]int{...}, ...) nu trebuie să scrieți Map[int, int](...): compilatorul deduce T=int, U=int.

Uneori inferența nu este suficientă (de exemplu, constructori fără argumente T) și este necesară instanțierea explicită: New[*Server]().

Constrângeri încorporate

ConstrângereÎnțeles
anyOrice tip (== interface{}).
comparableTipuri cu == și !=: numeric, șir, bool, pointeri, canale, matrice/structuri comparabile.

comparable permite egalitatea, dar nu < sau >.

Go
func Index[T comparable](s []T, x T) int {
    for i, v := range s {
        if v == x { return i }
    }
    return -1
}

Pentru </> aveți nevoie de o constrângere personalizată.

Constrângere personalizată: uniuni de tip

O constrângere este o interfață cu clauze „element de tip”:

Go
type Ordered interface {
    ~int | ~int64 | ~float64 | ~string
}

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = unire: acceptă oricare dintre tipurile enumerate.
  • ~int = „int sau orice tip definit ca type X int” (cu același tip de bază). Fără ~, doar exact int.

O constrângere poate, de asemenea, amesteca metode și elemente de tip:

Go
type Stringable interface {
    ~string
    Len() int
}

Tipuri generice

Nu numai funcții: structurile și interfețele pot avea și parametri de tip.

Go
type Set[T comparable] struct {
    m map[T]struct{}
}

func NewSet[T comparable]() *Set[T] {
    return &Set[T]{m: make(map[T]struct{})}
}

func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
func (s *Set[T]) Has(v T) bool { _, ok := s.m[v]; return ok }

s := NewSet[string]()
s.Add("ada")

Metodele NU POATE adăuga parametri de tip în afara celor de tip: func (s *Set[T]) MapTo[U any](...) nu se compilează (limitare intenționată de proiectare).

Când să folosiți generice

Da când:

  • Logica este cu adevărat identică pentru mai multe tipuri (colecții, algoritmi, ajutoare precum Map/Filter/Reduce).
  • Alternativa ar fi duplicarea codului sau utilizarea interface{} + aserțiuni de tip (pierderea siguranței tipului).

Nu când:

  • O interfață cu una sau două metode este suficientă și mai lizibilă (io.Reader, fmt.Stringer).
  • Parametrizați „pentru că puteți”: un singur caz de utilizare → faceți o versiune concretă a acestuia.

Exerciții

Exercițiu#go.m10.l3.e1
Încercări: 0Se încarcă…

Definisci la funcție generica Min[T Ordered](a, b T) T che ritorna il minore usando l'operatore <. Il constraint Ordered è già furnizor.

Se încarcă editorul...

Soluție disponibilă după 3 încercări

Exercițiu#go.m10.l3.e2
Încercări: 0Se încarcă…

Implementa Map[T, U any](s []T, f function(T) U) []U: aplica f a every element and return the new slice.

Se încarcă editorul...

Soluție disponibilă după 3 încercări

Quiz#go.m10.l3.e3
Gata

Cale constraint built-in accept l'operatore == ma NON l'operatore <?

Go
func Index[T ???](s []T, x T) int { ... }
Opțiuni de răspuns