タラバガニー設計局stalins.clubNOTE/notes/generic-methods

ジェネリクスメソッド (Go 1.27)

Go 1.27 で入った、メソッド自身が新しい型パラメータを宣言できる機能。提案は #77273 "A change of view."

Go 1.26 まで、型パラメータを新しく宣言できるのは関数と型だけだった。 レシーバ由来の型パラメータは使えたので「メソッドでジェネリクスが使えない」わけではなく、 使えなかったのはメソッドが型パラメータを増やすことだった。

func Map[T, U any](s []T, f func(T) U) []U // OK: 関数
type Stack[T any] struct{ items []T }      // OK: 型
func (s *Stack[T]) Push(v T)               // OK: レシーバ由来の T は使える

func (s *Stack[T]) Map[U any](f func(T) U) *Stack[U]
// NG! syntax error: method must have no type parameters

1.27 ではこれが書ける。spec の Method declarations に載っている公式例:

type List[E any] []E

func (l List[E]) Apply[F any](f func(E) F) List[F] {
	applied := make(List[F], 0, len(l))
	for _, v := range l {
		applied = append(applied, f(v))
	}
	return applied
}

効くのは 写像変換の形をしたメソッドである。List[E]E はレシーバから来るが、 変換先の F はレシーバのどこにも書かれていない。だから 1.26 まではメソッドにできず、 パッケージレベル関数に追い出すしかなかった。Push(v T) のように 「E を消費するだけ」のメソッドは昔から書けている。

何が変わって、何が変わっていないか

型の表現力は増えていない。List[F] を返すこと自体は関数版の Apply でもできていた。 変わるのは置き場所で、置き場所が変わると読み方と見つかり方が変わる。 提案本文が挙げている利点もそこにある。

x.a().b().c() may naturally be read left to right, whereas c(b(a(x))) is evaluated inside out.

実装方式も既存のジェネリクスと同じで、新しい機構は入っていない (GC shape stenciling と dictionary)。変わったのは 「メソッドは何のためにあるのか」という見方のほうである (メソッドはレシーバを持つ関数)。

代償

インターフェースを満たせない。これは実装上の難しさが解決されなかった結果を そのまま受け入れたもので、Go FAQ が却下した4案 が "unacceptable from a design point of view" として却下した案そのものである。 詳細は ジェネリクスメソッドはインターフェースを満たさない

同じ割り切りの裏返しとして、関数だと通りメソッドだと通らないシグネチャ の制限も残った。

現況 (2026-08-15 時点)

出典

  • #77273 spec: generic methods for Go
  • #79750 internal/goexperiment: delete Flags.GenericMethods
  • go1.27rc3 の spec (Method declarations) と Go Release Party 1.27 の発表資料リポジトリ

▸ ノート一覧に戻る