メソッド式とメソッド値
ジェネリクスメソッドでもメソッド式・メソッド値は変わらず使えるが、レシーバ側がジェネリック型の場合は先に instantiate しておく必要がある。
type List[E any] struct{ ... }
func (*List[E]) format[F any](E, F) string { ... }
List[string].format // => [F any](string, F) string OK
List.format // NG (List が未インスタンス化)
List.format を書いても [E any, F any](E, F) string のような、レシーバとメソッド両方の型パラメータを持つ関数にはならない。提案 #77273 本文の言葉を借りれば「メソッドがジェネリックなら、結果の関数もジェネリックになる」だけで、レシーバ側の型パラメータはメソッド式を作る前に確定していなければならない。
これは「レシーバは単なる第一引数」というモデルの反例のひとつでもある (メソッドはレシーバを持つ関数)。レシーバが本当にただの第一引数なら、List.format は E と F の両方を型パラメータに持つ関数になってよさそうだが、実際にはそうならない。レシーバの型引数とメソッド自身の型引数は、性質の異なる2つの経路として扱われている。
メソッド値が取れない場合があることは、実務上の互換性問題にも直結する。既存の非ジェネリックなメソッドに型パラメータを後付けすると、それまで書けていた
_ = g.Do
のようなメソッド値の取得がコンパイルエラーになる。これは言語機能としては正しい挙動だが、「型パラメータを足すだけの一見無害なリファクタ」が実は破壊的変更になる、という話につながる。詳細と実例 (x/sync/singleflight の Group.Do) は 既存メソッドへの型パラメータ後付けは破壊的変更 に譲る。
出典
- Go Release Party 1.27 の発表資料リポジトリ
memo.md§1 - #77273