Модульні уроки (3/5)
Generics (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 che ritorna il minore usando l'operatore <. Il constraint Ordered è già fornito.
Рішення доступне після 3 спроб
Implementa Map[T, U any](s []T, f func(T) U) []U: applica f a ogni elemento e restituisce il nuovo slice.
Рішення доступне після 3 спроб
Яке вбудоване обмеження погоджується з оператором == ma NON l'operatore <?
func Index[T ???](s []T, x T) int { ... }