stefafafan の fa は3つです

"すてにゃん" こと id:stefafafan のブログです

Go 1.18 に入った Generics の練習がてら math パッケージの関数を整数対応させてみる

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)))
}

xy を受け取り、内部でそれぞれ 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 に存在する FloatInteger を内包する interface にしたので、これで整数と浮動小数点数の両方に対応した Min ができました。

mathパッケージの他の主要な関数も同様に対応

同じ方式で対応したものを mathg と称してパッケージにしてみました。
github.com

テストケースもなくて雑なので、参考程度にみてくださいという感じです。

Go Playgroundでサードパーティのパッケージもインポートできると知って実際試してみましたが、思った通りに動いていそうですね。
go.dev

最初に目にしたイマイチなコードも今回作ったパッケージによってこのように置き換えられるようになりました。Genericsがあるのはありがたいですね。はやく標準パッケージもこういう感じになってほしい気がします。

- baz := math.Min(float64(foo), float64(bar))
+ baz := mathg.Min(foo, bar)