Уроки модуля (3/5)
Дженерики (Go 1.18+)
Начиная с Go 1.18 (март 2022 г.), доступны параметры типа: функции и типы можно параметризовать по типам. Они позволяют писать Map, Filter, Set[T], LinkedList[T] без дублирования кода и без interface{} + утверждений типа времени выполнения.
Общая функция: синтаксис
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 допускает равенство, но нет < или >.
func Index[T comparable](s []T, x T) int {
for i, v := range s {
if v == x { return i }
}
return -1
}Для </> вам потребуется пользовательское ограничение.
Пользовательское ограничение: объединения типов
Ограничение — это интерфейс с предложениями «элемент типа»:
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.
Ограничение также может смешивать методы и элементы типов:
type Stringable interface {
~string
Len() int
}Общие типы
Не только функции: структуры и интерфейсы также могут иметь параметры типа.
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).
- Вы параметризуете «потому что можете»: один вариант использования → создайте его конкретную версию.
Упражнения
Definisci la funzione generica Min[T Ordered](a, b T) T cheritorna il второстепенный usando l'operatore <. Ограничение Приказал и остался без работы.
Решение доступно после 3 попыток
Реализация Map[T, U Any](s []T, f func(T) U) []U: применение одного элемента и восстановление нового среза.
Решение доступно после 3 попыток
Встроенное ограничение Quale позволяет оператору == ma NON l'operatore <?
func Index[T ???](s []T, x T) int { ... }