Перейти к основному содержимому
Модуль 10 · Урок 3 из 548/50 в курсе~15 min
Уроки модуля (3/5)

Дженерики (Go 1.18+)

Начиная с Go 1.18 (март 2022 г.), доступны параметры типа: функции и типы можно параметризовать по типам. Они позволяют писать Map, Filter, Set[T], LinkedList[T] без дублирования кода и без interface{} + утверждений типа времени выполнения.

Общая функция: синтаксис

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}

Три новых элемента:

  • [T, U any] после имени: объявляет параметры типа с их ограничениями.
  • any = ограничение, которое принимает любой тип (псевдоним interface{}, начиная с Go 1.18).
  • Вывод: вызывая Map([]int{...}, ...), вам не нужно писать Map[int, int](...): компилятор выводит T=int, U=int.

Иногда вывода недостаточно (например, конструкторы без аргументов T) и требуется явное создание экземпляра: New[*Server]().

Встроенные ограничения

ОграничениеЗначение
anyЛюбой тип (== interface{}).
comparableТипы с == и !=: числовые, строковые, логические, указатели, каналы, сопоставимые массивы/структуры.

comparable допускает равенство, но нет < или >.

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

Для </> вам потребуется пользовательское ограничение.

Пользовательское ограничение: объединения типов

Ограничение — это интерфейс с предложениями «элемент типа»:

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

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = объединение: принимает любой из перечисленных типов.
  • ~int = «int или любой тип, определенный как type X int» (с тем же базовым типом). Без ~ только точно int.

Ограничение также может смешивать методы и элементы типов:

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

Общие типы

Не только функции: структуры и интерфейсы также могут иметь параметры типа.

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

Методы НЕ МОГУТ добавлять параметры типа, кроме параметров типа: func (s *Set[T]) MapTo[U any](...) не компилируется (намеренное ограничение конструкции).

Когда использовать дженерики

Да, когда:

  • Логика действительно идентична для нескольких типов (коллекций, алгоритмов, помощников типа Map/Filter/Reduce).
  • Альтернативой может быть дублирование кода или использование утверждений типов interface{} + (теряем безопасность типов).

Нет, когда:

— Интерфейс с одним-двумя методами достаточен и более читабелен (io.Reader, fmt.Stringer).

  • Вы параметризуете «потому что можете»: один вариант использования → создайте его конкретную версию.

Упражнения

Упражнение#go.m10.l3.e1
Попыток: 0Загрузка…

Definisci la funzione generica Min[T Ordered](a, b T) T cheritorna il второстепенный usando l'operatore <. Ограничение Приказал и остался без работы.

Загрузка редактора…

Решение доступно после 3 попыток

Упражнение#go.m10.l3.e2
Попыток: 0Загрузка…

Реализация Map[T, U Any](s []T, f func(T) U) []U: применение одного элемента и восстановление нового среза.

Загрузка редактора…

Решение доступно после 3 попыток

Quiz#go.m10.l3.e3
Готово

Встроенное ограничение Quale позволяет оператору == ma NON l'operatore <?

Go
func Index[T ???](s []T, x T) int { ... }
Варианты ответа