Introduction to Generics
Why Generics?
Before generics, writing a function that worked on both []int and []string meant either duplicating the function per type or falling back to interface{} and losing compile-time type safety. Generics solve this by letting one function definition work across multiple types while the compiler still checks types.
Example: Why Generics?
package main
import "fmt"
func PrintAll[T any](items []T) {
for _, item := range items {
fmt.Println(item)
}
}
func main() {
PrintAll([]int{1, 2, 3})
PrintAll([]string{"a", "b"})
}
Login to try C/C++/Java/PHP code in the editor
A Simple Generic Function
A generic function declares one or more type parameters in square brackets right after its name, which can then be used like any other type throughout the function's signature and body.
Example: A Simple Generic Function
package main
import "fmt"
func Max[T int | float64](a, b T) T {
if a > b {
return a
}
return b
}
func main() {
fmt.Println(Max(3, 7))
fmt.Println(Max(2.5, 1.1))
}
Login to try C/C++/Java/PHP code in the editor
Generics Are Still Type-Checked
Even though generic code is written once for many types, Go still fully type-checks each instantiation at compile time -- calling a generic function with a type that doesn't satisfy its constraints is a compile error, not a runtime surprise.
Example: Generics Are Still Type-Checked
package main
import "fmt"
func First[T any](items []T) T {
return items[0]
}
func main() {
fmt.Println(First([]string{"go", "generics"}))
fmt.Println(First([]bool{true, false}))
}
Login to try C/C++/Java/PHP code in the editor
- Assuming Go had generics from the start -- they were only added in Go 1.18 (2022), so older code and tutorials often avoid them entirely.
- Reaching for generics for every function, even when a plain interface or a couple of concrete-typed functions would be simpler.
- Forgetting the type parameter must be declared in square brackets before the regular parameter list.
- Generics, added in Go 1.18, let functions and types work with multiple types while staying type-safe.
- A type parameter is declared in square brackets before the regular parameters, like func Max[T int | float64](a, b T) T.
- Generics avoid duplicating near-identical code for each concrete type.
- Go still resolves generic code at compile time -- there's no runtime type-erasure surprise.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: