Modulelessen (3/5)
Generieke geneesmiddelen (Go 1.18+)
Sinds Go 1.18 (maart 2022) zijn typeparameters beschikbaar: functies en typen kunnen via typen worden geparametriseerd. Ze laten je Map, Filter, Set[T], LinkedList[T] schrijven zonder code te dupliceren en zonder interface{} + runtime-type beweringen.
Generieke functie: de syntaxis
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}Drie nieuwe elementen:
[T, U any]na de naam: declareert de typeparameters met hun beperkingen.any= beperking die elk type accepteert (alias vaninterface{}sinds Go 1.18).- Gevolgtrekking: als u
Map([]int{...}, ...)aanroept, hoeft uMap[int, int](...)niet te schrijven: de compiler leidt hieruitT=int, U=intaf.
Soms is gevolgtrekking niet voldoende (bijvoorbeeld constructors zonder T argumenten) en is expliciete instantiatie nodig: New[*Server]().
Ingebouwde beperkingen
| Beperking | Betekenis |
|---|---|
any | Elk type (== interface{}). |
comparable | Typen met == en !=: numeriek, string, bool, pointers, kanalen, vergelijkbare arrays/structs. |
comparable staat gelijkheid toe, maar niet < of >.
func Index[T comparable](s []T, x T) int {
for i, v := range s {
if v == x { return i }
}
return -1
}Voor </> heeft u een aangepaste beperking nodig.
Aangepaste beperking: type vakbonden
Een beperking is een interface met "type element"-clausules:
type Ordered interface {
~int | ~int64 | ~float64 | ~string
}
func Min[T Ordered](a, b T) T {
if a < b { return a }
return b
}|= unie: accepteert elk van de vermelde typen.~int= "int of een ander type gedefinieerd alstype X int" (met hetzelfde onderliggende type). Zonder~, alleen exactint.
Een beperking kan ook methoden en type-elementen combineren:
type Stringable interface {
~string
Len() int
}Generieke typen
Niet alleen functies: structs en interfaces kunnen ook typeparameters hebben.
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")Methoden KUNNEN GEEN typeparameters toevoegen die verder gaan dan die van het type: func (s *Set[T]) MapTo[U any](...) compileert niet (opzettelijke ontwerpbeperking).
Wanneer generieke geneesmiddelen gebruiken?
Ja wanneer:
- De logica is echt identiek voor verschillende typen (collecties, algoritmen, helpers zoals
Map/Filter/Reduce). - Het alternatief is het dupliceren van code of het gebruiken van
interface{}+ typebeweringen (waarbij de typeveiligheid verloren gaat).
Nee wanneer:
- Een interface met een of twee methoden is voldoende en beter leesbaar (
io.Reader,fmt.Stringer). - Je parametriseert "omdat het kan": een enkele use case → maak er een concrete versie van.
Oefeningen
Definieer de generieke functie Min[T Ordered](a, b T) T che ritorna il minore usando l'operatore <. Il constraint Besteld en già fornito.
Oplossing beschikbaar na 3 pogingen
Implementeer een kaart[T, U any](s []T, f func(T) U) []U: toepassing van een ander element en het nieuwe segment herstellen.
Oplossing beschikbaar na 3 pogingen
Ingebouwde quale constraint toestemming voor de operator == ma NON l'operator <?
func Index[T ???](s []T, x T) int { ... }