Go might finally get generic methods
Go generics shipped in 1.18, but methods were left with an awkward rule: a method can use type parameters from its receiver type, but it cannot declare fresh type parameters of its own.
That is why this still does not compile in today’s stable Go toolchains:
type Box[T any] struct {
value T
}
func (b Box[T]) Map[U any](f func(T) U) Box[U] {
return Box[U]{value: f(b.value)}
}
The compiler rejects Map[U any] because methods cannot have type parameters. The workaround is a top-level function:
func Map[T, U any](b Box[T], f func(T) U) Box[U] {
return Box[U]{value: f(b.value)}
}
That works, but it changes the shape of an API. You write Map(box, fn) instead of box.Map(fn), and fluent chains become inside-out helper calls.
The important update: the accepted proposal is now golang/go#77273, opened by Robert Griesemer in January 2026 and marked for the Go 1.27 milestone. The older long-running request, #49085, is now closed as a duplicate of that accepted proposal.
What the proposal allows
The syntax change is small. A concrete method declaration gets the same optional type parameter list that functions already have:
type Result[T any] struct {
value T
err error
}
func (r Result[T]) Map[U any](f func(T) U) Result[U] {
if r.err != nil {
return Result[U]{err: r.err}
}
return Result[U]{value: f(r.value)}
}
Then callers can use inference:
email := userResult.Map(func(u User) string {
return u.Email
})
Or they can write the type argument explicitly:
email := userResult.Map[string](func(u User) string {
return u.Email
})
That is the whole ergonomic win. You can keep method-based APIs when the method’s output type is different from the receiver’s input type.
What it does not allow
The proposal is deliberately about concrete methods. Interface methods are not changing.
This would still be invalid:
type Mapper[T any] interface {
Map[U any](func(T) U) Mapper[U]
}
And a concrete type with a generic method does not satisfy an interface method by “choosing” one instantiation:
type Readerish interface {
Read([]byte) (int, error)
}
type Reader struct{}
func (Reader) Read[T any]([]T) (int, error) {
return 0, nil
}
var _ Readerish = Reader{} // invalid
That distinction is the compromise that makes the proposal plausible for Go. Concrete method calls can be resolved statically. Interface dispatch is harder because Go interfaces are satisfied implicitly, and the runtime cannot know every possible instantiation of an infinite family of generic methods.
Why people care
The easy answer is “method chaining”, but the deeper answer is API locality. A method is attached to a type’s namespace. When someone types query. or result., their editor can show the operations that make sense for that value.
Without generic methods, Go libraries often have to choose between:
- top-level helpers that are type-safe but less discoverable
- receiver types with too many type parameters
anyplus assertions- code generation
Generic methods would make some of those APIs boring again.
Iterator-style code is the obvious example:
names := users.
Filter(func(u User) bool { return u.Active }).
Map(func(u User) string { return u.Name })
Result or Option types are another:
id := parseUser(raw).
Map(func(u User) int64 { return u.ID })
You may not want these patterns everywhere in Go. I do not. But when a library already owns a fluent API, the current restriction forces it into clumsier shapes.
The implementation cost
The proposal text is refreshingly plain about this. The spec change is small. The parser already understands much of the syntax. The type checker and compiler still need real work, and import/export data is one of the awkward parts because tools need to agree on how generic methods are represented.
That means two practical things:
First, do not start writing library designs that require this until it lands in a released Go toolchain.
Second, expect some tool lag. go/types, analyzers, formatters, linters, editors, and code generators all need to understand the new shape. The proposal itself says tools may take one or two release cycles to catch up.
The part I like
The proposal does not try to sneak higher-kinded types into Go. It does not make interfaces more powerful. It does not turn Go into a language where every collection wants a typeclass hierarchy.
It removes one restriction: concrete methods can be generic in the same way functions can be generic.
That is a pretty Go-shaped change. Small surface area, obvious syntax, real ergonomic payoff, and a hard line around the part that would make the implementation explode. If it ships in Go 1.27, expect the first wave of use in builders, iterators, validation libraries, result-ish APIs, and database query DSLs - the places where fluent code already exists, but generic functions keep getting in the way.