Generics Basics
10 examples to get you started with Generics - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Generics - 7 basic and 3 intermediate.
slices/maps/cmp examples need Go 1.21+.mkdir gdemo && cd gdemo && go mod init example.com/gdemo.main.go and run with go run ..A type parameter T with constraint cmp.Ordered enables < on numbers and strings.
package main
import (
"cmp"
"fmt"
)
func Max[T cmp.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
func main() {
fmt.Println(Max(3, 7), Max("a", "z"))
}cmp.Ordered is a stdlib constraint for ordered types.T from arguments at the call site.Related: Generics in Go: Constraints Without Templates - mental model for constraints
Pass type arguments when inference is ambiguous.
package main
import "fmt"
func Zero[T any]() T {
var z T
return z
}
func main() {
fmt.Printf("%v %q\n", Zero[int](), Zero[string]())
}any is the constraint that accepts all types.Zero[int]() substitutes T with int.T is returned without reflection.Related: Type Parameters & Constraint Interfaces -
anyand named constraints
Loop with index-free range over []T.
package main
import "fmt"
func Contains[T comparable](s []T, v T) bool {
for _, x := range s {
if x == v {
return true
}
}
return false
}
func main() {
fmt.Println(Contains([]string{"a", "b"}, "b"))
}comparable allows == and != on T.T itself cannot be a slice type in a comparable map-key role.slices.Contains from stdlib in production code.Related: Type Sets & the comparable Constraint - what comparable allows
Type parameters on struct types carry through methods.
package main
import "fmt"
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
v := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return v, true
}
func main() {
var st Stack[int]
st.Push(1)
v, _ := st.Pop()
fmt.Println(v)
}Stack[T] use the same T as the struct.Stack[int], Stack[string]) is a distinct type.Stack[int]{} is ready to use.Related: Generic Data Structures: Sets, Queues, Maps - reusable containers
Constraints can require methods, not only type unions.
package main
import "fmt"
type Stringer interface {
String() string
}
func Join[T Stringer](items []T, sep string) string {
if len(items) == 0 {
return ""
}
out := items[0].String()
for _, it := range items[1:] {
out += sep + it.String()
}
return out
}
type ID int
func (id ID) String() string { return fmt.Sprintf("id:%d", int(id)) }
func main() {
fmt.Println(Join([]ID{1, 2}, ","))
}T Stringer means every T must implement String() string.implements keyword.T are allowed because they are in the constraint.Related: Type Parameters & Constraint Interfaces - method constraints
Build a frequency table keyed by T.
package main
import "fmt"
func Count[T comparable](items []T) map[T]int {
m := make(map[T]int)
for _, v := range items {
m[v]++
}
return m
}
func main() {
fmt.Println(Count([]string{"a", "b", "a"}))
}comparable.Related: Type Sets & the comparable Constraint - map key rules
Short variable declaration picks the instantiation.
package main
import "fmt"
type Pair[A, B any] struct {
First A
Second B
}
func main() {
p := Pair[string, int]{"go", 1}
fmt.Println(p.First, p.Second)
}[A, B any].Related: Generic Data Structures: Sets, Queues, Maps - multi-parameter containers
Accept named types based on their underlying type.
package main
import "fmt"
type Celsius float64
type Fahrenheit float64
type Number interface {
~float64
}
func Double[T Number](v T) T {
return v * 2
}
func main() {
fmt.Println(Double(Celsius(10)), Double(Fahrenheit(20)))
}~float64 includes Celsius and Fahrenheit because their underlying type is float64.~, only the literal type float64 would match.Related: Type Parameters & Constraint Interfaces -
~operator depth
Union types list allowed concrete types.
package main
import "fmt"
type Integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
func Abs[T Integer](v T) T {
if v < 0 {
return -v
}
return v
}
func main() {
fmt.Println(Abs(int8(-3)), Abs(int64(-9)))
}| builds a union inside an interface constraint.Related: Type Parameters & Constraint Interfaces - union limits
Reach for stdlib generic helpers before custom copies.
package main
import (
"fmt"
"slices"
)
func main() {
nums := []int{1, 2, 3}
fmt.Println(slices.Contains(nums, 2))
fmt.Println(slices.Index(nums, 9))
}slices and maps packages are generic-first APIs in Go 1.21+.Related: Generic Algorithms: slices, maps & cmp Packages - stdlib catalog
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 19, 2026