在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创建了StringSetIntSet来自的结构ItemSet**,因为告诉了它Item=string,int

它还创建了struct方法构成我们的Set实现。

如果我们想向我们的应用程序添加另一组类型,我们可以编辑命令并再次运行它。

使用go generate

您也可以通过在文件顶部,包doc下方添加注释来自动执行此操作,例如:

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

这说明go generate,通过genny,以生成版本翻译Itemstring,以及第二个版本的翻译itemint

go generate

得到gen-set.go文件,带有字符串和整数集。

测验

重要提示:以上数据结构的通用实现编译并可以测试就像由...生成的具体实现一样容易go generate,因此我们可以针对通用实现以及特定于类型的测试进行测试。


更多教程: