Go 1.18でGenerics対応が入りましたが、個人的にはまだ活用できていませんでした。 https://go.dev/doc/go1.18#generics
そんな中、最近目にしたコードで、 math.Min
を使いたいがためにcastしている事例がありました。
// foo と bar はどっちも整数 baz := math.Min(float64(foo), float64(bar))
なるほど float64
しか渡せないんですね。 https://pkg.go.dev/math#Min
これはGenericsの出番じゃないですか?
Generics で実装してみる
実装してみるといっても math.Min
を再実装したいのではなく、 int
を渡したら内部で一旦float64
に変換してから math.Min
を呼んでその返り値を int
に変換して返すという方針でやってみます。
func Min[T Number](x, y T) T { return T(math.Min(float64(x), float64(y))) }
x
と y
を受け取り、内部でそれぞれ float64
に変換して math.Min
を呼びます。最後に元の T
にcastしなおしてから返すようになっています。
また、 Number
というものは存在しなくて、これも別で定義しました。
import ( "golang.org/x/exp/constraints" ) type Number interface { constraints.Float | constraints.Integer }
https://pkg.go.dev/golang.org/x/exp/constraints に存在する Float
と Integer
を内包する interface にしたので、これで整数と浮動小数点数の両方に対応した Min
ができました。
mathパッケージの他の主要な関数も同様に対応
同じ方式で対応したものを mathg
と称してパッケージにしてみました。
github.com
テストケースもなくて雑なので、参考程度にみてくださいという感じです。
Go Playgroundでサードパーティのパッケージもインポートできると知って実際試してみましたが、思った通りに動いていそうですね。
go.dev
最初に目にしたイマイチなコードも今回作ったパッケージによってこのように置き換えられるようになりました。Genericsがあるのはありがたいですね。はやく標準パッケージもこういう感じになってほしい気がします。
- baz := math.Min(float64(foo), float64(bar)) + baz := mathg.Min(foo, bar)