Что такое дженерики (generics) в Go и зачем они нужны?

Ответ

Дженерики (обобщённое программирование) — это возможность писать функции и структуры данных, которые могут работать с любым типом, удовлетворяющим определённым условиям. Эта возможность появилась в Go версии 1.18.

Основная цель дженериков — избежать дублирования кода для разных типов данных, сохраняя при этом строгую типизацию.

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

  1. Параметры типа (Type Parameters): Объявляются в квадратных скобках после имени функции. [T any] означает, что T — это параметр типа, который может быть любым типом.
  2. Ограничения типа (Type Constraints): Интерфейсы, которые определяют, какие операции разрешены для параметра типа. Например, встроенное ограничение comparable позволяет использовать операторы == и !=.

Классический пример — функция для суммирования чисел:

Раньше пришлось бы писать две функции: SumInts и SumFloats. С дженериками можно написать одну.

// Определяем ограничение, которое разрешает использовать int или float64
type Number interface {
    int | int64 | float32 | float64
}

// SumNumbers работает с любым типом, подходящим под ограничение Number
func SumNumbers[T Number](a, b T) T {
    return a + b
}

func main() {
    // Использование с int
    sumInt := SumNumbers(5, 10) // Результат: 15
    fmt.Println(sumInt)

    // Использование с float64
    sumFloat := SumNumbers(3.14, 2.71) // Результат: 5.85
    fmt.Println(sumFloat)
}

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

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

Когда использовать:

  • При работе с коллекциями: слайсами, картами, каналами.
  • При написании общих алгоритмов: сортировка, поиск, фильтрация.
  • В структурах данных, таких как деревья, списки, графы.