Что такое Generics (обобщения) в Go и зачем они нужны?

Ответ

Generics (обобщения), появившиеся в Go 1.18, — это механизм, который позволяет писать функции и типы данных, способные работать с любым из набора типов, не дублируя код. Основная цель — повышение переиспользуемости кода и обеспечение строгой типизации.

Ключевые концепции:

  1. Параметры типа (Type Parameters): Объявляются в квадратных скобках [T any]. T — это имя параметра типа, а any — его ограничение (constraint).
  2. Ограничения (Constraints): Это интерфейсы, которые определяют, какие типы могут быть использованы в качестве аргумента типа. any — это псевдоним для interface{}, разрешающий любой тип.

Пример: Обобщенная функция

Эта функция может напечатать слайс любого типа:

// T - параметр типа, any - ограничение (любой тип)
func PrintSlice[T any](s []T) {
    for _, v := range s {
        fmt.Println(v)
    }
}

func main() {
    PrintSlice([]int{1, 2, 3})
    PrintSlice([]string{"a", "b", "c"})
}

Пример с ограничением:

Можно создать ограничение, чтобы функция работала только с типами, поддерживающими определенные операции. Например, для суммирования чисел:

// Определяем интерфейс-ограничение
// (в стандартной библиотеке есть constraints.Ordered)
type Number interface {
    int | int64 | float32 | float64
}

// Функция работает только с типами, удовлетворяющими Number
func Sum[T Number](numbers []T) T {
    var total T
    for _, n := range numbers {
        total += n
    }
    return total
}

Преимущества:

  • Код без дублирования (DRY): Одна функция для разных типов.
  • Типобезопасность: Компилятор проверяет типы во время компиляции, в отличие от interface{}.