Golang пощупаем дженерики
Скоро выйдет релиз 1.18 в котором появятся долгожданные дженерики. Они позволят сделать универсальные методы. Я написал пару примеров для себя. Может быть они будут интересны кому-нибудь ещё.
Интерфейсы any, comparable, constraints. и ~
Появились новые ключевые слова
any — аналог interface{}
. Это ключевое слово можно использовать в любом месте. Например при определении типа переменной или при опрредении типа поля в структуре. Вот такой код ошибок не вызовет:
func TestDo(v any) any {
var k any
k = 10
return k
}
type Test interface {
Some() any
}
comparable — это интерфейс который определяет типы которые могут бысь сравнены с помощью == и !=. Переменные такого типа создать нельзя. (var j comparable
будет вызывать ошибку.)
Появилась возможность определять интерфейсы, которые можно будет использовать в параметризованных функциях и типах. Переменные такого типа создать нельзя. (var j Int
будет вызывать ошибку.)
type Int interface {
int | int32 | int64
}
Под данный интерфейс подходят только описанные в нём тиипы.
Если добавить знак ~ перед типами то интерфейсу будут соотвествовать и производные типы, например myInt из примера ниже:
type Int interface {
~int | ~int32 | ~int64
}
type myInt int
Разработчики golang создали для нас уже готовый набор интерфейсов, котрый очень удобно использовать: https://pkg.go.dev/golang.org/x/exp/constraints
Параметризованные функции
Давайте рассмотрим пример функции Max. Эта функция возвращает максимум из двух переданных значений. Причём тип может быть любым.
import "constraints"
func Max[T constraints.Ordered](a T, b T) T {
if a > b {
return a
}
return b
}
Ограничения на используемые типы описываются в квадратных скобочках. В качестве огранияения для типов можно использовать любой интерфейс и особые интерфейсы описанные выше.
Давайте проведём эксперимент и посмотрим какой тип будет у параметра параметризованной функции:
package main
import (
"fmt"
"reflect"
)
func TypeTest[T any](a T) {
fmt.Println(reflect.TypeOf(a))
}
func main() {
TypeTest("abc")
TypeTest(1.0)
TypeTest(1)
}
Результат соответствует ожиданиям:
string
float64
int
Для слайсов и мапов был создан набор готовых полезных функций:
https://pkg.go.dev/golang.org/x/exp/slices
https://pkg.go.dev/golang.org/x/exp/maps
Параметризованные типы
Давайте сделаем пример мапы и добавим пару методов. Один из которых вытащит все ключи, а второй напишет в консоль тип.
package main
import (
"fmt"
"reflect"
)
type myMap[K comparable, V any] map[K]V
func (m myMap[K, V]) Keys() []K {
res := make([]K, 0, len(m))
for k := range m {
res = append(res, k)
}
return res
}
func (m myMap[K, V]) Type() {
fmt.Println(reflect.TypeOf(m))
}
func main() {
mp := myMap[int, string]{5: "sd"}
fmt.Println(mp.Keys())
mp.Type()
}
Вывод:
[5]
main.myMap[int,string]
Немного моих мыслей
Сейчас появятся библиотеки которые будут реализовывать функции Max, Min, IIF и подобные. Вот мой пример
А так же появятся библиотеки реализующие Where, Order, Any и подобные методы для мапов и слайсов.
Я думаю что дженерики стоит использовать только в самых общих библиотеках. Их использование в логике будет создавать хаос. Но найдутся те, кто со мной не согласен.