← Назад к вопросам

Как относишься к дженерикам?

2.0 Middle🔥 172 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой взгляд на дженерики в Go

Как Senior Go-разработчик с большим опытом, я отношусь к дженерикам позитивно, но с изрядной долей прагматизма. Их введение в Go 1.18 стало одним из самых значимых изменений в языке за последние годы, и у этого решения есть как неоспоримые преимущества, так и определённые компромиссы.

Сильные стороны дженериков

Типобезопасность и устранение дублирования кода — главные выгоды. До дженериков мы часто использовали interface{} или кодогенерацию для создания универсальных структур и функций:

// Старый подход с interface{}
func Contains(slice []interface{}, item interface{}) bool {
    for _, v := range slice {
        if v == item {
            return true
        }
    }
    return false
}

// С дженериками
func Contains[T comparable](slice []T, item T) bool {
    for _, v := range slice {
        if v == item {
            return true
        }
    }
    return false
}

Повышение читаемости и безопасности типов — компилятор теперь может проверять типы на этапе компиляции, что уменьшает количество runtime-ошибок. Особенно полезно это для коллекций и утилитарных функций:

// Типобезопасные коллекции
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() T {
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item
}

Области эффективного применения

  • Утилитарные функции работы с коллекциями — функции типа Map, Filter, Reduce становятся типобезопасными
  • Структуры данных — реализации деревьев, стеков, очередей, кэшей
  • Алгоритмы — сортировки, поисковые алгоритмы, математические операции
  • Инфраструктурный код — кэширование, пулы соединений, middleware

Ограничения и умеренность

Go остаётся языком с минималистичной реализацией дженериков, что соответствует философии языка. В сравнении с шаблонами C++ или дженериками C#, Go сознательно ограничивает возможности:

  • Нет специализации шаблонов (template specialization)
  • Нет ковариантности/контравариантности параметров
  • Нет дженериков для методов (только для типов и функций)
  • Ограничения выражаются через интерфейсы, что иногда приводит к многословности

Практические рекомендации

  1. Избегайте преждевременной дженерификации — если код используется с одним-двумя типами, дженерики могут быть избыточны
  2. Документируйте сложные ограничения — когда используете сложные interface-ограничения, добавляйте комментарии
  3. Тестируйте с разными типами — особенно важно для пользовательских типов, реализующих интерфейсы-ограничения
  4. Следите за читаемостью — сложные дженерик-конструкции могут ухудшить понимание кода

Пример сбалансированного подхода

// Полезное применение: обработка разных числовых типов
type Number interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
    ~float32 | ~float64
}

func Sum[T Number](numbers []T) T {
    var total T
    for _, n := range numbers {
        total += n
    }
    return total
}

// А здесь дженерики не нужны - достаточно интерфейса
type Processor interface {
    Process() error
}

func Run(p Processor) error {
    return p.Process()
}

Заключение

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

Я использую дженерики там, где они приносят реальную пользу: в библиотеках, инфраструктурном коде, утилитарных функциях. В бизнес-логике часто достаточно интерфейсов. Важно помнить, что дженерики увеличивают сложность понимания кода, поэтому их применение должно быть оправдано и сопровождаться хорошей документацией и тестами.

Как относишься к дженерикам? | PrepBro