go generics の練習


これは何?

go 1.18 が正式リリースになり。
以前から無くて困っていた generics がようやく入ったので、その練習

最大値関数

math.Maxfloat64 しか受け入れない厳しい世界からようやく脱却。

go1.18
package main

import (
	"fmt"
	"time"

	"golang.org/x/exp/constraints"
)

func Max2[T constraints.Ordered](x, y T) T {
	if x < y {
		return y
	}
	return x
}

func main() {
	fmt.Println(Max2(1, 2))                                       //=> 2
	fmt.Println(Max2(1.11, 2.22))                                 //=> 2.22
	fmt.Println(Max2("hoge", "fuga"))                             //=> hoge
	fmt.Println(Max2(333*time.Millisecond, 444*time.Millisecond)) //=> 444ms
}

ここに到達するまでだいぶ時間がかかった。
constraints"constraints" ではなく "golang.org/x/exp/constraints" になった模様。

constraints.Ordered は 比較演算子( < とか)が使えるもの全部ということらしい。

big.Int でも Max したい

int 用と float32 用を同じ定義で書けることがはわかった。
しかし big.Int とかもある。もちろん big.Int を上記の Max2 に入れたらエラー。

go1.18
Max2(big.NewInt(1), big.NewInt(2))
//=> *big.Int does not implement constraints.Ordered

まあそうだよね。むしろそうじゃなきゃ困る。
やりたいことは

go.1.18(希望)
a := Max2(1, 2)
b := Max2("hoge", "fuga")
c := Max2(big.NewInt(1), big.NewInt(2))

ができる Max2 を定義すること。
それが無理なら以下のような

go.1.18(せめてこれぐらい)
x := BigMax2(big.NewInt(1), big.NewInt(2))
y := BigMax2(big.NewRat(10, 7), big.NewRat(14, 10))
z := BigMax2(big.NewFloat(1), big.NewFloat(2))

BigMax2 を定義したい。これぐらいは書けるべきと思う。

しかしまだ書けていない。

「引数の型がレシーバと同じであるメソッド Cmp を持つ型 T
という制約が必要だと思うんだけど、今の所これが書けない。

難しい。

進展があったら加筆する予定。

BigMax への道(🍊未完🍊)

Generics がない場合

big.Int だけなら、こうする。

go1.18
package main

import (
	"fmt"
	"math/big"
)

func BigMaxInt2(x, y *big.Int) *big.Int {
	if x.Cmp(y) < 0 {
		return y
	}
	return x
}

func main() {
	fmt.Println(BigMaxInt2(big.NewInt(1), big.NewInt((2))))
}

この BigMaxInt2 の型 big.Int を引数にすればいいと思うよね。

first trial (失敗)

まずはこう書くと

go1.18
type BigNumber interface {
	big.Int | big.Float | big.Rat
}

func BigMax2[T BigNumber](x, y *T) *T {
	// ↓ x.Cmp undefined (type *T is pointer to type parameter, not type parameter)
	if x.Cmp(y) < 0 { 
		return y
	}
	return x
}

エラー。ちょっと何を言っているのかわからない。なんかわからないけどこうしてみると

go1.18
type BigNumber interface {
	*big.Int | *big.Float | *big.Rat
}

func BigMax2[T BigNumber](x, y T) T {
	// ↓ x.Cmp undefined (type T has no field or method Cmp)
	if x.Cmp(y) < 0 {
		return y
	}
	return x
}

メッセージが変わった。
でもどうしたらいいのかわからない。