Краткое руководство по дженерикам Go в Go 1.18+

Введение

Начиная с Go 1.18 были введены универсальные типы.

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

Если вы не знаете, что это значит, не волнуйтесь, мы доберемся до этого!

Функция

Учитывая функцию

func Foo(t any) { }

мы можем сделать его общим, добавив [T any] к подписи следующим образом

func Foo[T any](t T) {}

Затем мы можем вызвать функцию с конкретным типом, который мы хотим

F[string](“hello, generics!”) // t is a string at compile time

Структура

Учитывая тип

type Bar struct {
    t any
}

мы можем сделать его универсальным

type Bar[T any] struct {
    t T
}

а затем создайте экземпляр с конкретным типом, который мы хотим

b := Bar[string] {
t: "hello, generics" // b.t is a string type
}

b.t — это строка во время выполнения в этом примере.

Метод

К сожалению, у методов не может быть дженериков. Они могут использовать только дженерики, представленные в типе, в котором они находятся. Следуя нашему примеру выше, мы можем определить общий метод следующим образом:

func (b Bar[T]) Foo() { }

Интерфейс

Интерфейсы работают точно так же, как структуры.

Учитывая интерфейс

type Fooer interface {
    Bar(any)
}

мы можем сделать этот общий вот так

type Fooer[T any] interface {
    Foo(T)
}

а затем создайте экземпляр и вызовите метод

// StringFooer is a struct with the method Foo(string)
var f Fooer[string] := StringFooer{}
f.Foo("hello, generics")

Ограничения

[T any], который мы использовали, ограничивает T типом any.

Мы также можем ограничить T другими типами.

[T any] // T can be any type
[T int] // T can only be an int
[T ~int] // T can only be an int OR any type with an underlying type of int, e.g. type I int
T[T int | string] // T can only be an int OR string
T[T io.Reader] // T can be any type that implements io.Reader

Иногда вы хотите определить ограничение для многих типов или просто хотите определить его один раз.

Мы можем использовать синтаксис интерфейса для этого

type Float interface {
	~float32 | ~float64
}

Важно отметить: это ограничение, НЕ интерфейс. Вы можете использовать интерфейс в качестве ограничения, но вы не можете использовать ограничение в качестве интерфейса!

который мы затем можем использовать как

[T Float] // Reader challenge: what types are accepted here?

Предопределенные ограничения

Стандартная библиотека предоставляет некоторые предопределенные ограничения, такие как comparable .

Дополнительные можно найти на https://pkg.go.dev/golang.org/x/exp/constraints, пока они не будут объединены в stdlib (1.19+).

Заключение

Это в основном все, что вам нужно знать о дженериках в Go, чтобы начать.

Если у вас есть что-то, что, по вашему мнению, должно быть включено, пожалуйста, оставьте комментарий.

Если вы нашли эту информацию полезной и хотите больше обсудить Go, присоединяйтесь ко мне и другим пользователям Go Discord Server по адресу discord.gg/golang.

Не забудьте поставить 👏 и подписаться на другие материалы Go!