Przejdź do głównej treści
eLearner.app
Moduł 10 · Lekcja 3 z 548/50 w kursie~15 min
Lekcje modułu (3/5)

Generyki (Go 1.18+)

Od wersji Go 1.18 (marzec 2022 r.) dostępne są parametry typów: funkcje i typy można parametryzować według typów. Umożliwiają pisanie Map, Filter, Set[T], LinkedList[T] bez duplikowania kodu i bez asercji typu interface{} + środowiska wykonawczego.

Funkcja ogólna: składnia

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}

Trzy nowe elementy:

  • [T, U any] po nazwie: deklaruje parametry typu wraz z ich ograniczeniami.
  • any = ograniczenie akceptujące dowolny typ (alias interface{} od wersji 1.18).
  • Wnioskowanie: wywołując Map([]int{...}, ...) nie musisz wpisywać Map[int, int](...): kompilator wnioskuje T=int, U=int.

Czasami wnioskowanie nie wystarczy (np. konstruktory bez argumentów T) i konieczna jest jawna instancja: New[*Server]().

Wbudowane ograniczenia

OgraniczenieZnaczenie
KODEF0Dowolny typ (== interface{}).
KODEF2Typy z == i !=: numeryczne, string, bool, wskaźniki, kanały, porównywalne tablice/struktury.

comparable pozwala na równość, ale nie < lub >.

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

Dla </> potrzebujesz niestandardowego ograniczenia.

Ograniczenie niestandardowe: wpisz związki

Ograniczeniem jest interfejs z klauzulami „element typu”:

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

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = unia: akceptuje dowolny z wymienionych typów.
  • ~int = „int lub dowolny typ zdefiniowany jako type X int” (z tym samym typem bazowym). Bez ~, tylko dokładny int.

Ograniczenie może również mieszać metody i elementy typu:

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

Typy ogólne

Nie tylko funkcje: struktury i interfejsy mogą mieć również parametry typu.

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")

Metody NIE MOGĄ dodawać parametrów typu poza typami: func (s *Set[T]) MapTo[U any](...) nie kompiluje (zamierzone ograniczenie projektu).

Kiedy używać nazw generycznych

Tak gdy:

  • Logika jest naprawdę identyczna dla kilku typów (kolekcji, algorytmów, pomocników, takich jak Map/Filter/Reduce).
  • Alternatywą byłoby powielenie kodu lub użycie asercji typu interface{} + (utrata bezpieczeństwa typu).

Nie gdy:

  • Wystarczający i bardziej czytelny jest interfejs z jedną lub dwiema metodami (io.Reader, fmt.Stringer).
  • Parametryzujesz „ponieważ możesz”: pojedynczy przypadek użycia → utwórz jego konkretną wersję.

Ćwiczenia

Ćwiczenie#go.m10.l3.e1
Próby: 0Ładowanie...

Definisci la funzione rodzajowy Min[T Ordered](a, b T) T che ritorna il minore usando l'operatore <. Il constraint Ordered jest dostępny.

Ładowanie edytora...

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m10.l3.e2
Próby: 0Ładowanie...

Zaimplementuj Map[T, U any](s []T, f func(T) U) []U: aplikacja dla każdego elementu i przywrócenie nowego plasterka.

Ładowanie edytora...

Rozwiązanie dostępne po 3 próbach

Quiz#go.m10.l3.e3
Gotowe

Wbudowane ograniczenie jakości pozwala operatorowi == ma NON operatora <?

Go
func Index[T ???](s []T, x T) int { ... }
Opcje odpowiedzi