Go: Дженерики

Теория: Универсальные функции

Обычная функция в Go работает с заранее заданным типом. Универсальная функция использует параметр типа и применяет одну и ту же логику к разным данным. Такой параметр указывается в квадратных скобках после имени функции и затем участвует в сигнатуре.

Функция может обобщаться как по одному типу, так и по нескольким сразу. Когда нужен только один параметр, он объявляется в квадратных скобках и используется в сигнатуре вместо конкретного типа:

func Foo[T any](value T) T

Здесь параметр T связывает аргумент и возвращаемое значение: тип входа и выхода будет одним и тем же. Если же требуется больше гибкости, можно ввести несколько параметров. Они указываются через запятую в скобках. Например, функция, создающая пару из двух значений разных типов:

func MakePair[K, V any](k K, v V) (K, V)

Параметры типа можно использовать в любых частях сигнатуры: среди аргументов, в возвращаемых значениях, внутри структур и методов. Они играют ту же роль, что и обычные типы, только определяются не заранее, а в момент вызова функции. Если функция принимает срез []T, то она обязана вернуть результат того же типа T, что был подставлен при вызове. Это гарантирует согласованность и строгую проверку типов.

Пример функции поиска минимального элемента показывает, как параметр типа можно ограничить операциями сравнения. Для этого используют constraints.Ordered:

import "golang.org/x/exp/constraints"

func Min[T constraints.Ordered](a, b T) T {
	if a < b {
		return a
	}
	return b
}

Здесь T подходит для чисел и строк. Вызов Min(3, 7) вернёт 3, а Min("go", "java") — строку "go".

Другой пример — поиск значения в срезе. Здесь параметр T участвует и в типе элементов, и в типе искомого значения:

func Contains[T comparable](slice []T, target T) bool {
	for _, v := range slice {
		if v == target {
			return true
		}
	}
	return false
}

Такое описание не позволит искать строку в срезе чисел — строгая типизация сохраняется.

Функция реверса показывает, что параметр типа может использоваться только для элементов, а логика при этом остаётся общей:

func Reverse[T any](slice []T) []T {
	n := len(slice)
	result := make([]T, n)
	for i, v := range slice {
		result[n-1-i] = v
	}
	return result
}

Она одинаково работает для []int, []string и любых других срезов.

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

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845