Passer au contenu principal
eLearner.app
Module 10 · Leçon 3 sur 548/50 dans le cours~15 min
Leçons du module (3/5)

Génériques (Go 1.18+)

Depuis Go 1.18 (mars 2022), des paramètres de type sont disponibles : les fonctions et les types peuvent être paramétrés sur les types. Ils vous permettent d'écrire Map, Filter, Set[T], LinkedList[T] sans dupliquer le code et sans assertions de type d'exécution interface{} +.

Fonction générique : la syntaxe

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}

Trois nouveaux éléments :

  • [T, U any] après le nom : déclare les paramètres de type avec leurs contraintes.
  • any = contrainte qui accepte tout type (alias de interface{} depuis Go 1.18).
  • Inférence : en appelant Map([]int{...}, ...), vous n'avez pas besoin d'écrire Map[int, int](...) : le compilateur déduit T=int, U=int.

Parfois, l'inférence n'est pas suffisante (par exemple, des constructeurs sans arguments T) et une instanciation explicite est nécessaire : New[*Server]().

Contraintes intégrées

ContrainteSignification
anyTout type (== interface{}).
comparableTypes avec == et != : numérique, chaîne, booléen, pointeurs, canaux, tableaux/structures comparables.

comparable autorise l'égalité mais pas < ou >.

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

Pour </>, vous avez besoin d'une contrainte personnalisée.

Contrainte personnalisée : tapez les unions

Une contrainte est une interface avec des clauses "type element" :

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

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = union : accepte n'importe lequel des types répertoriés.
  • ~int = "int ou tout type défini comme type X int" (avec le même type sous-jacent). Sans ~, seulement int exact.

Une contrainte peut également mélanger des méthodes et des éléments de type :

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

Types génériques

Pas seulement les fonctions : les structures et les interfaces peuvent également avoir des paramètres de type.

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

Les méthodes NE PEUVENT PAS ajouter de paramètres de type au-delà de ceux du type : func (s *Set[T]) MapTo[U any](...) ne compile pas (limitation intentionnelle de la conception).

Quand utiliser des génériques

Oui quand :

  • La logique est vraiment identique pour plusieurs types (collections, algorithmes, helpers comme Map/Filter/Reduce).
  • L'alternative serait de dupliquer le code ou d'utiliser des assertions de type interface{} + (perte de la sécurité du type).

Non quand :

  • Une interface avec une ou deux méthodes suffit et plus lisible (io.Reader, fmt.Stringer).
  • Vous paramétrez "parce que vous pouvez" : un seul cas d'usage → en faire une version concrète.

Exercices

Exercice#go.m10.l3.e1
Tentatives : 0Chargement…

Définit la fonction générique Min[T Ordered](a, b T) T qui ritorna le mineur en utilisant l'opérateur <. La contrainte Ordonné est già fornito.

Chargement de l'éditeur…

Solution disponible après 3 tentatives

Exercice#go.m10.l3.e2
Tentatives : 0Chargement…

Implémentez Map[T, U any](s []T, f func(T) U) []U : appliquez chaque élément et restituez la nouvelle tranche.

Chargement de l'éditeur…

Solution disponible après 3 tentatives

Quiz#go.m10.l3.e3
Prêt

Quelle contrainte intégrée autorise l'opérateur == ma NON l'opérateur <?

Go
func Index[T ???](s []T, x T) int { ... }
Options de réponse