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 и подобные методы для мапов и слайсов.

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


ссылка на оригинал статьи https://habr.com/ru/post/651229/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *