Ответ
Дженерики (generics), появившиеся в Go 1.18, позволяют писать функции и структуры данных, которые могут работать с любыми типами, сохраняя при этом строгую проверку типов на этапе компиляции.
Основные проблемы, которые решают дженерики:
Избавление от дублирования кода. Раньше для работы с разными типами приходилось писать отдельные функции для каждого из них (например,
SortInts
,SortStrings
). Дженерики позволяют создать одну универсальную функцию.Типобезопасность без
interface{}
. До дженериков для написания обобщенного кода использовалиinterface{}
. Это приводило к двум проблемам:- Потеря статической проверки типов: приходилось использовать утверждения типа (
value.(string)
), которые могли вызвать панику в рантайме. - Снижение производительности из-за упаковки (boxing) значений в интерфейсы.
- Потеря статической проверки типов: приходилось использовать утверждения типа (
Пример универсальной функции Map
:
// T и U — это параметры типа (type parameters).
// any — это ограничение (constraint), означающее "любой тип".
func Map[T, U any](input []T, f func(T) U) []U {
output := make([]U, len(input))
for i, v := range input {
output[i] = f(v)
}
return output
}
// Пример использования:
numbers := []int{1, 2, 3}
// Компилятор сам выводит типы, код типобезопасен.
strings := Map(numbers, strconv.Itoa) // Результат: []string{"1", "2", "3"}
Таким образом, дженерики делают код более чистым, переиспользуемым и безопасным, устраняя необходимость в бойлерплейте и небезопасных преобразованиях типов.