Generic Methods
Since Go 1.27, a method can declare its own type parameters, separate from those of its type. That makes it possible to write list.Map(strconv.Itoa), turning a List[int] into a List[string] — something that previously had to be a package-level function. This lesson shows how generic methods are declared and called, where they're useful, and the rules that limit them.
The problem before Go 1.27
A method could use its type's parameters, but couldn't introduce new ones. So an operation that changes the element type had to live outside the type, as a function:
package main import ( "fmt" "strconv" ) type List[T any] struct{ items []T } // Before Go 1.27: a separate function, because the result type U is new. func MapList[T, U any](l List[T], f func(T) U) List[U] { out := List[U]{items: make([]U, 0, len(l.items))} for _, v := range l.items { out.items = append(out.items, f(v)) } return out } func main() { nums := List[int]{items: []int{1, 2, 3}} fmt.Printf("%q\n", MapList(nums, strconv.Itoa).items) }
["1" "2" "3"]
It works, but calls read inside-out, and the function sits in the package's namespace instead of with the type it belongs to.
Declaring a generic method
Put a type parameter list after the method name, exactly as for a function. The method can use both its own parameters and its receiver type's:
package main import ( "fmt" "strconv" "strings" ) type List[T any] struct{ items []T } func Of[T any](items ...T) List[T] { return List[T]{items: items} } func (l List[T]) Map[U any](f func(T) U) List[U] { out := List[U]{items: make([]U, 0, len(l.items))} for _, v := range l.items { out.items = append(out.items, f(v)) } return out } func (l List[T]) Filter(keep func(T) bool) List[T] { // an ordinary method var out List[T] for _, v := range l.items { if keep(v) { out.items = append(out.items, v) } } return out } func main() { words := Of(3, 14, 15, 92, 65). Filter(func(n int) bool { return n > 10 }). Map(strconv.Itoa). Map(func(s string) string { return "#" + s }) fmt.Println(strings.Join(words.items, " ")) lengths := Of("go", "gopher").Map[int](func(s string) int { return len(s) }) fmt.Println(lengths.items) }
#14 #15 #92 #65 [2 6]
As with functions, the type argument is usually inferred from the arguments; write it explicitly, as in Map[int](…), when you want to be clear or inference isn't possible. Calls now chain from left to right, in the order things happen.
Generic methods in the standard library
Go 1.27 added a generic method to math/rand/v2: (*Rand).N returns a random number below n of the same type as n, matching the package's top-level rand.N function:
package main import ( "fmt" "math/rand/v2" "time" ) func main() { r := rand.New(rand.NewPCG(7, 7)) fmt.Println(r.N(10)) // int fmt.Println(r.N(int64(1_000_000))) // int64 fmt.Println(r.N(500 * time.Millisecond)) // time.Duration }
9 19463 379.513046ms
Generic methods need Go 1.27
Like other new language features, generic methods are enabled by the go line in go.mod. In a module that says go 1.26 or earlier, the compiler refuses them even with a newer toolchain:
module example go 1.26
package main type Box[T any] struct{ v T } func (b Box[T]) Convert[U any](f func(T) U) Box[U] { return Box[U]{f(b.v)} } func main() {}
./main.go:5:25: generic method requires go1.27 or later (-lang was set to go1.26; check go.mod)
Update the line with go get go@1.27 (or edit it) to use them.
The rules
Generic methods are deliberately limited, so that interfaces keep working as before:
- Interface methods can't have type parameters. An interface can only list ordinary methods.
- A generic method never satisfies an interface method. Even if the names and shapes look alike, a type's generic
Map[U any]can't be what an interface'sMaprefers to.
package main type Mapper interface { Map[U any](f func(int) U) []U } func main() {}
./main.go:4:5: interface method must have no type parameters ./main.go:4:25: undefined: U
The reason is practical: to call a method through an interface, the program must find the code at run time, but a generic method needs a separate compiled version for each type argument — which can't be known in advance for every type that might ever implement the interface.
When to use a generic method
Use one when the operation naturally belongs to a type and needs a type that the type itself doesn't have: converting or mapping elements (Map), folding to a different result type (Reduce), decoding into a caller-chosen type. If the method only uses the type's own parameters, it's an ordinary method. If it doesn't really belong to one type, a package-level generic function is still fine — the slices and maps packages are all functions.
Try it yourself
Add a generic Reduce[A any](start A, f func(A, T) A) A method to List[T] and use it to total word lengths and to build a sentence.
21 Go: generic methods arrived