11 вопросов
С Go 1.18: параметризация типов и функций типами. Объявление: func F[T any](x T) T или type Box[T any] struct { V T }. Позволяет писать общий код для разных типов без interface{} и приведения типов. Компилятор генерирует специализированный код для каждого используемого типа.
Переиспользование кода для разных типов без копирования и без потери типобезопасности. Типобезопасные контейнеры (слайсы, map), общие алгоритмы (min, max, sort), уменьшение приведения типов и reflection. Интерфейсы остаются для поведения; дженерики - когда важен конкретный тип, но логика одна.
Интерфейсы - когда важно поведение (методы), тип может быть разным в runtime. Дженерики - когда нужна одна логика для разных типов, тип известен на этапе компиляции. Пример: io.Reader - интерфейс; функция func First[T any](s []T) T - дженерик. Часто комбинируют: ограничение типа дженерика интерфейсом.
Ограничение задаёт, какие типы можно подставлять в параметр типа. Встроенные: any (любой), comparable (сравнимые). Интерфейс как ограничение: func F[T io.Reader](r T) - только типы с методом Read. Пакет golang.org/x/exp/constraints: Ordered, Integer и др. Синтаксис: func F[T Constraint](...) .
Тильда в интерфейсе ограничений: ~T означает "любой тип с underlying type T". Например, type MyInt int удовлетворяет ~int, но не int. Используется в constraints: type Number interface { ~int | ~int64 | ~float64 } - все числовые типы с таким базовым типом.
Метод не может иметь своих параметров типа - только тип получателя может быть параметризован. То есть func (r T) M[U any]() не допускается. Параметры типа задаются у типа: type S[T any] struct{}; func (s S[T]) M(). Для "дженерик-метода" делают функцию-обёртку с параметром типа.
Компилятор выводит параметры типа по аргументам вызова. slices.Clone(s) выводит тип элемента из s. Явно: F[int](42). Вывод работает по аргументам и по результатам (если нужно). Нельзя вывести - нужно указать тип явно: F[int]().
Встроенное ограничение comparable - типы, которые можно сравнивать с == и !=. Нужно для ключей map и для обобщённых функций сравнения. Не сравнимы: слайсы, map, функции. Сравнимы: базовые типы, указатели, каналы, интерфейсы, структуры и массивы из сравниваемых полей.
Методы не могут иметь свои параметры типа. Нет специализации (specialization). Нет ковариантности/контравариантности. Нельзя использовать type assertion к параметру типа в runtime (тип стирается). Дженерики не заменяют интерфейсы - те остаются для полиморфизма в runtime.
С Go 1.18+: slices (Sort, Clone, Contains, Equal и др.), maps (Keys, Values, Clone, Equal), sync (Pool с типом), container/list и др. получили обобщённые варианты или новые API. Пакеты slices и maps - основные примеры использования дженериков в stdlib.
Да. Компилятор генерирует код для каждого набора параметров типа (monomorphization). Больше использований разных типов - больше кода. Обычно прирост небольшой; дублирование ограничено реально используемыми комбинациями. Оптимизация: избегать лишних параметров типа и не специализировать по множеству типов без нужды.