概要
Golang での Generics に関する知見をまとめた記事になります。
Generics とは?
ジェネリック(総称あるいは汎用)プログラミング)は、具体的なデータ型に直接依存しない、抽象的かつ汎用的なコード記述を可能にするコンピュータプログラミング手法である。
Golang では1.18からGenericsを利用できるようになりました。
引用元
比較
公式のドキュメントを元にGenerics有無のソースコードの比較と書き方を確認します
Generics を利用しないソースコード
package main
import "fmt"
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Non-Generic Sums: %v and %v\n",
SumInts(ints),
SumFloats(floats))
}
// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
仮にint64
, float64
以外の型を追加する場合に新たな関数を追加する必要がある。
Generics
を用いた場合、上記の問題を回避できる(汎用的な関数を作成できる)。
Generics を利用したソースコード
package main
import "fmt"
type Number interface {
int64 | float64
}
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Generic Sums: %v and %v\n",
SumIntsOrFloats[string, int64](ints),
SumIntsOrFloats[string, float64](floats))
fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
SumIntsOrFloats(ints),
SumIntsOrFloats(floats))
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
SumNumbers(ints),
SumNumbers(floats))
}
// SumIntsOrFloats sums the values of map m. It supports both floats and integers
// as map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
// SumNumbers sums the values of map m. Its supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
Type Parameters
関数の引数の前に[]
内に記述され、型を記述できます。
現状はany
とcomparable
がビルドインされていて、上記のソースコードではcomparable
が定義されています。
comparable
とは、型の値に ==
および !=
演算子を使用できるようにする制約です。
参考:Type parameters
Type Sets
以下の様に方パラメータを制約したい時にインターフェースを定義することができる。
type Number interface {
int64 | float64
}
SumIntsOrFloats
と SumNumbers
関数の引数は実質同じだと考えられるが、引数が多数存在する場合はインターフェースを定義した方が可読性が上がりそう。