タラバガニー設計局stalins.clubNOTE/notes/generic-method-not-satisfy-interface

ジェネリクスメソッドはインターフェースを満たさない

Go 1.27 でジェネリクスメソッドが入った代わりに受け入れられた妥協点が、これだ。インターフェースにはジェネリクスメソッドを書けない。

type Mapper interface {
	Map[U any](f func(int) U) []U // NG: インターフェースには書けない
}

そしてジェネリクスメソッドを持つ型があっても、そのメソッドはインターフェース充足の判定に使われない。

func (*Reader) Read[E any](buf []E) (int, error)
// NG: Read[byte] がいても io.Reader を満たさない
//     have Read[E any]([]E) (int, error)
//     want Read([]byte) (int, error)

Read[E any]([]E)Read([]byte) は、E にどんな型を代入しても []E[]byte が構文的に identical になることはないので、io.Reader を満たすと判定されることは永遠にない。

この効く範囲はメソッド単位であることに注意がいる。ジェネリック型が持つ普通の(型パラメータを追加しない)メソッド、たとえば func (Gen[P]) B(p P) のようなものは、従来どおりインターフェースを満たす。制約がかかるのは、メソッド自身が新しい型パラメータを宣言した場合だけだ。

この割り切りが実際に人を困らせている例もある。#79834 (2026-06-04, closed) で ianlancetaylor はこう答えている。

That is correct. This is the compromise that was accepted when generic methods were added to the language: generic methods do not satisfy interface types.

Go FAQ が却下した4案 が "unacceptable from a design point of view" と切り捨てた4番目の案の、実際の姿がこれだ。FAQ の4案の議論では抽象的な選択肢の一つだったものが、Go 1.27 では現実の制約として定着している。

一方で、この割り切りは静的解析側では単純化として効いている。x/tools の callgraph/rta はジェネリクスメソッドを呼び出しグラフの候補から最初から除外していて、コード中のコメントも端的だ。「skip generic methods since interfaces don't have them」。インターフェース経由の呼び出しは常に static に解決できるため、動的ディスパッチ (invoke) の心配をする必要がない。詳しくは 静的解析から見たジェネリクスメソッド

ただしツール側の実装はこの非対称にきれいには追従できていない。ジェネリクスメソッドは types.MethodSet (メソッドセット) には入るのに、インターフェースは満たさないという食い違いが、実際に x/tools のコードで nil パニックを引き起こした。go/ssa の MethodValue は generic method に対して nil を返す仕様になっており、types.MethodSet を回してそのまま MethodValue を呼ぶ定番イディオムが軒並み落ちた。

なお reflect からもジェネリクスメソッドは Value.MethodByName で取れない。ただしこれは「ジェネリクスメソッドだから」というより、「ジェネリクスは実体化された型でしか実行時に存在しない」という Go 1.18 からの一般的な制約に近い側面が強い。

メソッドの分類全体(メソッドの4象限)で見ると、ジェネリクスメソッドはこの「インターフェースを満たさない」象限に固有に位置づけられる。さらにこの妥協は、後述する 関数だと通りメソッドだと通らないシグネチャ の制限が緩和されない理由の裏付けにもなっている。「理屈上は緩和できるが、そうすると混乱を招く」という判断の根拠が、まさにこの非対称そのものだからだ。

出典

#79834 Go Release Party 1.27 の発表資料リポジトリ slide/deck.md (cant-interface スライドとスピーカーノート)、memo.md §1・§2、issues.md §7。

▸ ノート一覧に戻る