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:

GO
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)
}
Output
["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:

GO
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)
}
Output
#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:

GO
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
}
Output
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:

GO.MOD
module example

go 1.26
GO
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() {}
Compiler error
./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's Map refers to.
GO
package main

type Mapper interface {
	Map[U any](f func(int) U) []U
}

func main() {}
Compiler error
./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.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
21
Go: generic methods arrived
Next: iterators and range-over-func.