Generics был выпущен с версией Go 1.18. В основном это означает параметризованные типы, что означает, что он позволяет программистам писать код, в котором тип может быть указан позже, потому что тип в этот момент не имеет значения. Другими словами, при написании кода вы не указываете тип значений. Эти значения типа передаются позже.

Его синтаксис:

func funcName[type_parameter type_constraint](… type_parameter) type_parameter {
 …
}

func funcName[T any](… T) T {
 …
}

func funcName[T interface{}](… T) T {
 …
}

Здесь T — это параметр типа, а any — это ограничение типа, которое может быть любым интерфейсом, означает неограниченные значения, здесь any представляет собой пустой интерфейс.

В приведенном ниже примере есть две функции, первая returnFirst принимает любое ограничение, что означает, что мы можем передать что-либо, не являющееся конкретным int или float, и она возвращает первый аргумент. Вторая функция, а именно returnFloatFirst, принимает в качестве аргумента только тип float64, и если мы передадим любой другой тип, она выдаст ошибку. В основной функции вы можете видеть, что функцию returnFirst мы можем вызывать без указания типа ограничения, и она работает, это связано с выводом типа.

func returnFirst[T any](a T, b T) T {
  return a 
}

func returnFloatFirst[T float64](a T, b T) T {
  return a 
}

func main() {
  fmt.Println(returnFirst[int](1,3))
  fmt.Println(returnFirst(1,3))
  fmt.Println(returnFirst[float64](1.8,3.9))
  fmt.Println(returnFirst(1.8,3.9))
  fmt.Println(returnFirst("a","b"))
  fmt.Println(returnFloatFirst(1.2,3.4))
}

Go 1.18 поставляется с выводом типов, который помогает нам писать код, вызывающий универсальные функции без явных типов.

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

func sumAll[T any](arr []T) T {
  var s T
  for _, ele := range arr {
    s += ele
  }
  return s
}

func main() {
  fmt.Println("sum: ", sumAll([]int{1, 2, 3, 5, 6}))
}

Когда вы запустите приведенный выше код, он выдаст ошибку:

$ go run main.go
./main.go:6:9: invalid operation: operator + not defined on a (variable of type T constrained by any)

Это связано с тем, что любое ограничение может содержать любое значение, здесь, в приведенном выше случае, это int, но это может быть что угодно, и есть вероятность, что оператор может не работать для этого конкретного типа. поэтому выдает ошибку. Чтобы решить эту проблему, мы используем набор типов, где мы определяем пользовательское ограничение с помощью интерфейса и используем его вместо ограничения типа. Мы объявляем набор типов для этого ограничения и должны использовать только эти типы.

Синтаксис для определения пользовательского ограничения:

type customConstraint interface {
  type1 | type2 | type3 …
} 

type cusConstraint interface {
  float64 | int | string
}

Мы также можем использовать пакет constraints, который определяет набор полезных ограничений, которые будут использоваться с параметрами

Сначала мы должны установить пакет ограничений.

$ go get golang.org/x/exp/constraints

Некоторые ограничения из пакета.

type Signed interface {
  ~int | ~int8 | ~int16 | ~int32 | ~int64
}

type Unsigned interface {
  ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

type Integer interface {
  Signed | Unsigned
}

type Float interface {
  ~float32 | ~float64
}

type Complex interface {
  ~complex64 | ~complex128
}

type Ordered interface {
  Integer | Float | ~string
}

Итак, теперь давайте создадим пользовательское ограничение, которое может поддерживать типы данных float64 и int, и используем его вместо любого ограничения.

type constraint interface {
  ~float64 | int
}

func sumAll[T constraint](arr []T) T {
  var s T
  for _, ele := range arr {
    s += ele
  }
  return s
}

func main() {
  fmt.Println("sum: ", sumAll([]int{1, 2, 3, 5, 6}))
  fmt.Println("sum: ", sumAll([]float64{1.2, 2.1, 3.8, 5.4}))
}

Приведенный выше код работает.

Спасибо, что написали эту часть, пожалуйста, добавьте свои ценные комментарии и предложения, чтобы я мог сделать статью более интерактивной.