🙌

Go の Generics でできないこと

2022/09/08に公開

はじめに

Go 1.18 で導入された Generisc を試してみて色々と分かったことがあるのでまとめます。
そもそもの仕様が理解不足な面もありますが、とりあえず当たったパターンを書いていきます。

参考文献にも挙げた Go言語のジェネリクス入門(1) でおっしゃられているように、 Generics の基本原則を抑えておけば今回のような誤解は解けそうです。

  • 「関数」と「型」は「型パラメータ」を持つことができる。

もし理解違いがありましたら教えて下さいませ〜🙏

環境

Go: 1.19.0

メソッドに型パラメータを持つことはできない

メソッドに型パラメータを持つことはできません。

型パラメータを持つメソッドの定義は可能です。
呼び出すことはできません。
(この後紹介する例のように厳密には異なるのですが、実運用としては上記の解釈で間違いありません)

type Value()

func (v *Value) method[T any]() {}

func main() {
    v := &Value{}
    
    // Invalid operation: 'v.method[string]' (type 'func[T any]()' does not support indexing)
    // Type 'string' is not an expression
    v.method[string]()
}

あまり意味はないですが、型パラメータの指定を省略すれば呼び出しは可能です。
その場合は構文エラーにはなりません。

type Value()

func (v *Value) method[T any]() {}

func main() {
    v := &Value{}

    v.method()
}

解決策としては、レシーバを受け取らない素の関数にすることです。

func method[T any]() {}

func main() {
    method[string]()
}

型パラメータを持つ関数型の DefinedType で、右辺で型パラメータを利用することはできない

型パラメータを持つ関数型の DefinedType で、右辺で型パラメータを利用することはできません。
表現がややこしいですが、以下の例では保持した型パラメータを DefinedType の引数の型として利用しています。
(上手い表現もしくは、definedType の右辺を表す固有名詞があれば教えて下さい!)

type funcType[T any] = func(arg T)

func main(f funcType[string]) {
    // Cannot call the nonfunction f that has the type funcType[string]
    f("str")
}

DeifnedType の引数の型ではなく、戻り値の型として利用した場合も同様です。

type funcType[T any] = func() T

func main(f funcType[string]) {
    // Cannot call the nonfunction f that has the type funcType[string]
    str := f()
}

解決策としては、関数自体で型パラメータを受け取り、関数型をインラインで直接定義すれば呼び出すことが可能です。
(あまり解決策とは呼べませんが、、、)

func main[T any](t T, f func(arg T) ) {
    f(t)
}

まとめ

良き Go ライフを過ごしましょう!!

参考文献

Discussion