Go Generic Methods
A generic method is a method that carries its own type parameters. The method can work with a new type while the value it hangs on keeps its own type.
A Map method with its own type parameter
List holds ints here. Its Map method adds a type parameter U and builds strings from the ints.
package main
import "fmt"
type List[T any] struct {
items []T
}
func (l List[T]) Map[U any](f func(T) U) []U {
result := make([]U, len(l.items))
for i, v := range l.items {
result[i] = f(v)
}
return result
}
func main() {
numbers := List[int]{items: []int{1, 2, 3}}
words := numbers.Map(func(n int) string {
return fmt.Sprintf("%d!", n)
})
fmt.Println(words)
}A Filter method on the same List
Filter adds no new type parameter. It works with the T from List and keeps the values its test accepts.
package main
import "fmt"
type List[T any] struct {
items []T
}
func (l List[T]) Filter(keep func(T) bool) []T {
var result []T
for _, v := range l.items {
if keep(v) {
result = append(result, v)
}
}
return result
}
func main() {
numbers := List[int]{items: []int{1, 2, 3, 4, 5, 6}}
evens := numbers.Filter(func(n int) bool {
return n%2 == 0
})
fmt.Println(evens)
}A generic method on a plain type
Box is not a generic type. Its Show method still takes a type parameter and prints a value of any type beside the label.
package main
import "fmt"
type Box struct {
label string
}
func (b Box) Show[T any](value T) {
fmt.Println(b.label, value)
}
func main() {
box := Box{label: "Value:"}
box.Show(42)
box.Show("go")
}Example
package main
import "fmt"
type List[T any] struct {
items []T
}
func (l List[T]) Map[U any](f func(T) U) []U {
result := make([]U, len(l.items))
for i, v := range l.items {
result[i] = f(v)
}
return result
}
func (l List[T]) Filter(keep func(T) bool) []T {
var result []T
for _, v := range l.items {
if keep(v) {
result = append(result, v)
}
}
return result
}
func main() {
numbers := List[int]{items: []int{1, 2, 3, 4}}
words := numbers.Map(func(n int) string {
return fmt.Sprintf("%d!", n)
})
kept := List[string]{items: words}.Filter(func(s string) bool {
return s != "3!"
})
fmt.Println(kept)
}Key points
- A method may carry its own type parameters, apart from the parameters of its receiver type.
- The receiver type parameters come from the type the method hangs on.
- Map on List[T] adds a second parameter U for the values it builds.
- A method with no new type job can use the receiver parameter alone, as Filter does.
- A plain non-generic type can also host a generic method.
In this example, Map first turns each int into a string with an exclamation mark. The strings move into a new List, and Filter drops the one equal to 3!. The kept strings print in order.
Note
A generic method is called like any other method, with the dot and the method name.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท Go Playground
Exercise
Where do the receiver type parameters of a generic method come from?