Go generics
last modified May 5, 2025
In this article we show how to work with generics in Golang.
Generics in Go enable developers to write flexible and reusable code that can operate on multiple data types. By using generics, functions and types can be defined in a way that allows them to work with any specified set of types, reducing redundancy and improving maintainability.
Go implements generics through parameterized types, which are defined inside
square brackets []
. These type parameters allow developers to
specify constraints while still maintaining flexibility.
func Println[T any](v T) { fmt.Println(v) }
In this example, the Println
function accepts a generic type
parameter T
. The keyword any
is used as a constraint,
indicating that T
can be any type. When called, the function prints
its argument while maintaining type safety.
By convention, generic parameter types are represented with uppercase letters,
most commonly T
for a general type, K
for a key, and
V
for a value. This standard improves readability and consistency
across Go programs.
Generics are particularly beneficial when designing libraries, data structures, and algorithms that require type flexibility while preserving strong typing and efficiency.
Go generics example
In the first example, we define a simple generic function.
package main import ( "fmt" ) func Println[T any](v T) { fmt.Println(v) } func main() { Println[string]("an old falcon") Println[int](23) Println[float64](3.34) Println[bool](true) Println[[]int]([]int{1, 2, 3, 4, 5}) }
Our custom Println
function can accept any type and print it.
Println[string]("an old falcon") Println[int](23) Println[float64](3.34) Println[bool](true) Println[[]int]([]int{1, 2, 3, 4, 5})
We print a string value, an int64 value, a float64 value, a bool value and a integer slice value.
$ go run main.go an old falcon 23 3.34 true [1 2 3 4 5]
Go generics omit type
In many cases, we can omit the type when calling the generic function. The compiler will infer the type from the function arguments.
package main import ( "fmt" ) func Println[T any](v T) { fmt.Println(v) } func main() { Println("an old falcon") Println(23) Println(3.34) Println(true) Println([]int{1, 2, 3, 4, 5}) }
In the program we omit the parameter type declarations when calling the
Println
function.
$ go run main.go an old falcon 23 3.34 true [1 2 3 4 5]
Go generic union type
We can restrict the generic type parameter to the types in the union.
package main import ( "fmt" ) func Println[T string | int](v T) { fmt.Println(v) } func main() { Println("an old falcon") Println(23) // Println(3.34) // Println(true) // Println([]int{1, 2, 3, 4, 5}) }
In the program, the Println
function can accept either strings or
integers.
$ go run main.go an old falcon 23
Go generics tilde
The ~
tilde token is used in the form ~T
to denote the
set of types whose underlying type is T
.
package main import ( "fmt" ) type mystring string func Println[T ~string | int](v T) { fmt.Println(v) } func main() { Println("an old falcon") Println(23) Println(mystring("rainy day")) }
In the program, we have a custom mystring
type. With
~string
syntax, we tell the compiler to include any type that
approximates to string
.
$ go run main.go an old falcon 23 rainy day
Go generic filter function
The filter function processes a collection and produces a new collection containing exactly those elements for which the given predicate returns true.
In the next example, we create a generic version of the filter function.
package main import ( "fmt" "strings" ) func filter[T any](data []T, f func(T) bool) []T { fltd := make([]T, 0, len(data)) for _, e := range data { if f(e) { fltd = append(fltd, e) } } return fltd } func main() { words := []string{"war", "cup", "water", "tree", "storm"} res := filter(words, func(s string) bool { return strings.HasPrefix(s, "w") }) fmt.Println(res) vals := []int{-1, 0, 2, 5, -9, 3, 4, 7} res2 := filter(vals, func(e int) bool { return e > 0 }) fmt.Println(res2) }
In the program we use a generic filter function to filter strings and integers.
func filter[T any](data []T, f func(T) bool) []T { fltd := make([]T, 0, len(data)) for _, e := range data { if f(e) { fltd = append(fltd, e) } } return fltd }
The filter function builds a new slice which includes only elements that satisfy
the given condition. The function works on parameter type T
with
constraint any
. It takes a collection and a predicate function as
parameters. We call the predicate on each element and add it to the
fltd
slice if it matches the predicate's condition.
res := filter(words, func(s string) bool { return strings.HasPrefix(s, "w") })
Here we filter out all words that start with 'w'.
$ go run main.go [war water] [2 5 3 4 7]
Go generic ForEach function
In the next program, we create a generic ForEach function.
package main import "fmt" func ForEach[T any](data []T, f func(e T, i int, data []T)) { for i, e := range data { f(e, i, data) } } func main() { vals := []int{-1, 0, 2, 1, 5, 4} ForEach(vals, func(e int, i int, data []int) { fmt.Printf("e at %d: %d\n", i, e) }) fmt.Println("-------------------------") words := []string{"sky", "forest", "word", "cup", "coin"} ForEach(words, func(e string, i int, data []string) { fmt.Printf("e at %d: %s\n", i, e) }) }
The generic ForEach function takes a generic slice and a closure function as parameters. The closure is used to perform a task on each of the elements.
func ForEach[T any](data []T, f func(e T, i int, data []T)) { for i, e := range data { f(e, i, data) } }
In the ForEach
function, we use the for
loop to
go over the elements of the generic slice and call the closure on each element.
ForEach(vals, func(e int, i int, data []int) { fmt.Printf("e at %d: %d\n", i, e) })
When actually calling the ForEach
function, we pass a closure with
concrete types. The elements have int
type, the index has
int
type, and the collection is of int[]
type.
$ go run main.go e at 0: -1 e at 1: 0 e at 2: 2 e at 3: 1 e at 4: 5 e at 5: 4 ------------------------- e at 0: sky e at 1: forest e at 2: word e at 3: cup e at 4: coin
Source
Getting started with generics - tutorial
In this article we have covered generics in Golang.
Author
List all Go tutorials.