Зарегистрируйтесь, чтобы продолжить обучение

Универсальные функции 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 и любых других срезов.

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


Самостоятельная работа

Потренируемся в написании простой универсальной функции без ограничений.

  1. Реализуйте фнукцию Repeat[T any](v T, n int) []T, которая создаёт срез из n копий значения v.

  2. Проверьте на нескольких типах (например, int и string).

Показать решение
package main

import "fmt"

func Repeat[T any](v T, n int) []T {
    if n <= 0 {
        return []T{}
    }
    xs := make([]T, n)
    for i := 0; i < n; i++ {
        xs[i] = v
    }
    return xs
}

func main() {
    fmt.Println(Repeat(7, 3))    // [7 7 7]
    fmt.Println(Repeat("go", 2)) // [go go]
}

Дополнительные материалы

  1. Спецификация — Объявления функций
  2. Блог Go — Введение в дженерики
  3. Документация — Руководство по дженерикам
  4. Спецификация — Параметры типа (Type parameters)

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff