Материал проходит вычитку — возможны неточности.
Урок 63 из 118

Границы дженериков †

На ревью Аня осадила Мишу: в коде появился дженерик, под которым под разными типами пряталось разное поведение. Она вернула PR с комментарием: «Повторяется логика, а не просто тип».

Главная мысль

у дженериков есть границы — до Go 1.26 включительно методы не могли иметь собственные параметры типа (с Go 1.27 разрешены), производительность зависит от конкретного случая, а применять их стоит там, где повторяется логика, а не просто тип.

Чего дженерики не умеют

Методы не могли иметь собственные параметры типа до Go 1.26 включительно; с Go 1.27 это разрешено. Дженерик-функция и дженерик-тип есть всегда, а вот метод с отдельным [T] внутри обычного типа на версиях до 1.27 — нет. Если методу нужен свой тип, а код собирается старыми версиями, обходят это либо выносом логики в дженерик-функцию, либо делая сам тип дженериком. Аня показывает Мише именно такой обходной путь в его коде — он корректен для версий, на которых живёт «Полка».

Производительность — без гарантий

В Go нет классической мономорфизации C++, где каждая инстанциация — отдельный скомпилированный код. Вместо этого компилятор использует формы (GC shape stenciling) и словари для типов — деталь реализации. Из-за этого «дженерики быстрее или медленнее» зависит от конкретного случая. Мериют бенчмарком, а не рассуждением (инструмент — в).

Когда дженерики уместны

Главный ориентир — повторяется ли логика, а не просто тип. Если функция делает одно и то же для int и для string, и разница только в типе — дженерик оправдан. Если под разными типами скрывается разное поведение, дженерик лишь спрячет эту разницу за switch по типам — и тогда честнее интерфейс. Именно такой случай Аня и нашла на ревью.

Для контейнеров и мелких утилит вроде chunk, filter, contains дженерики хороши. Для доменной логики, где общность надумана, они приносят больше шума, чем пользы. Стандартная библиотека так и поступила: добавила slices, maps и cmp с дженериками, но не переписала на них всё подряд.

В следующем уроке

Вы узнаете:

  • чем дженерик отличается от подхода io.Reader
  • что дают пакеты slices, maps, cmp
Задание · вопросы

Проверьте себя

Вопрос 1 из 3

Метод func (b Box) Map[T any](...) компилируется?

Вопрос 2 из 3

Сортировка слайсов разных числовых типов — дженерик или интерфейс?

Вопрос 3 из 3

Что известно о производительности дженериков в Go?

Итоги

Границы: методы без своих типов-параметров до Go 1.26 включительно (с Go 1.27 разрешены); производительность — через бенчмарк; применять там, где общая логика, а не общий тип.

Наш сайт собирает куки. Чтобы тебе было удобнее им пользоваться. Нажимая на кнопку, ты даёшь разрешение собирать куки