在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,因此我們可以針對通用實現以及特定於類型的測試進行測試。


更多教程: