Создание реализаций для универсальных типов в Go

Go имеет сильные статические типы и не поддерживает универсальные шаблоны, поэтому как мы можем определить структуры данных и алгоритмы общего назначения, которые можно применить к более чем одному типу?interface{}s - не решение, так как они требуют приведения типов, и мы теряем многие преимущества сильной статической типизации.Решение - генерация кода, поскольку это позволяет получить проверки и безопасность во время компиляции, а также более высокую производительность.

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

Проблема:

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

Решение:

С помощьюДженни, это не могло быть проще:

  1. импортgenny/generic
  2. определить один или несколько типов какgeneric.Type, например, позвонив имItemили жеType
  3. используйте эти типы в своем коде
  4. пробегgennyдля генерации реализации для конкретного типа

Пример

Посмотрите на этот простой пример, который представляет собой урезанную версиюУстановить реализацию структуры данных в Go:

// Package set creates a ItemSet data structure for the Item type
package set

import github.com/cheekybits/genny/generic

// Item the type of the Set type Item generic.Type

// ItemSet the set of Items type ItemSet struct { items map[Item]bool }

// Add adds a new element to the Set. Returns the Set. func (s ItemSet) Add(t Item) ItemSet { if s.items == nil { s.items = make(map[Item]bool) } _, ok := s.items[t] if !ok { s.items[t] = true } return s }

// Clear removes all elements from the Set func (s ItemSet) Clear() { (s).items = make(map[Item]bool) }

Запуская

genny -in set.go -out gen-set.go gen "Item=string,int"

в командной строке теперь, если файл называетсяset.goэто сгенерируетgen-set.goкоторый содержит следующее:

// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny

// Package Set creates a StringSet data structure for the string type
package set

// StringSet the set of Strings type StringSet struct { items map[string]bool }

// Add adds a new element to the Set. Returns the Set. func (s StringSet) Add(t string) StringSet { s.items[t] = true return s }

// Clear removes all elements from the Set func (s StringSet) Clear() { (s).items = make(map[string]bool) }

// Delete removes the string from the Set and returns Has(string) func (s StringSet) Delete(item string) bool { ret := (s).Has(item) if ret { delete((*s).items, item) } return ret }

// Has returns true if the Set contains the string func (s StringSet) Has(item string) bool { return (s).items[item] }

// Strings returns the string(s) stored func (s *StringSet) Strings() []string { items := []string{} for i := range s.items { items = append(items, i) } return items }

// Package Set creates a IntSet data structure for the int type // IntSet the set of Ints type IntSet struct { items map[int]bool }

// Add adds a new element to the Set. Returns the Set. func (s IntSet) Add(t int) IntSet { s.items[t] = true return s }

// Clear removes all elements from the Set func (s IntSet) Clear() { (s).items = make(map[int]bool) }

// Delete removes the int from the Set and returns Has(int) func (s IntSet) Delete(item int) bool { ret := (s).Has(item) if ret { delete((*s).items, item) } return ret }

// Has returns true if the Set contains the int func (s IntSet) Has(item int) bool { return (s).items[item] }

// Ints returns the int(s) stored func (s *IntSet) Ints() []int { items := []int{} for i := range s.items { items = append(items, i) } return items }

Как вы видете,gennyсоздалStringSetиIntSetструктуры изItemSet**, потому что сказал этоItem=string,int.

Он также создал методы структурыкоторые составляют наши реализации Set.

Если мы хотим добавить еще один набор типов в наше приложение, мы можем отредактировать команду и запустить ее снова.

Использоватьgo generate

Вы также можете автоматизировать это, добавив комментарий вверху файла под документом пакета, например:

// go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Item=string,int"

Это говоритgo generate, с помощьюgenny, чтобы создать версию, переводящуюItemкstring, а вторая версия переводитitemкint.

Пробег

go generate

чтобы получитьgen-set.goфайл со строковыми и целыми наборами.

Тестирование

Важно: общая реализация структуры данных вышекомпилируется и может быть протестировантак же легко, как конкретная реализация, созданнаяgo generate, поэтому мы можем запускать тесты как для нашей общей реализации, так и для конкретных типов.


Больше руководств по go: