🧬 Go – Generics Explained with Syntax, Examples & Use Cases (2025 Guide)
🧲 Introduction – What Are Generics in Go?
Generics allow you to write functions, types, and data structures that work with any data type while maintaining type safety. Introduced in Go 1.18, generics eliminate the need for boilerplate code, reduce duplication, and bring parametric polymorphism to Go.
🎯 In this section, you’ll learn:
- How to define generic functions and types
- What type parameters and constraints are
- How to use interfaces as constraints
- Practical examples and common patterns
✅ Basic Generic Function Syntax
func PrintSlice[T any](s []T) {
for _, v := range s {
fmt.Println(v)
}
}
✅ T
is a type parameter, and any
is a constraint allowing any type.
🔁 Using Generic Function
PrintSlice([]int{1, 2, 3})
PrintSlice([]string{"Go", "Generics"})
📤 Output:
1
2
3
Go
Generics
✅ Same function works for both int
and string
without rewriting it.
🧱 Defining Generic Types (Structs)
type Box[T any] struct {
Value T
}
✅ A Box
can hold a value of any type, while preserving compile-time safety.
b1 := Box[int]{Value: 100}
b2 := Box[string]{Value: "Hello"}
fmt.Println(b1.Value) // 100
fmt.Println(b2.Value) // Hello
📏 Using Constraints
type Number interface {
~int | ~float64
}
func Add[T Number](a, b T) T {
return a + b
}
✅ The ~
means the type must be based on that type (or its alias).
fmt.Println(Add(3, 4)) // 7
fmt.Println(Add(2.5, 1.5)) // 4.0
📦 Using comparable
Constraint
func IndexOf[T comparable](slice []T, item T) int {
for i, v := range slice {
if v == item {
return i
}
}
return -1
}
✅ comparable
ensures ==
is valid for that type.
fmt.Println(IndexOf([]string{"a", "b", "c"}, "b")) // 1
🧪 Generic Map Function
func Map[T any, R any](in []T, f func(T) R) []R {
out := make([]R, len(in))
for i, v := range in {
out[i] = f(v)
}
return out
}
squares := Map([]int{1, 2, 3}, func(x int) int {
return x * x
})
fmt.Println(squares) // [1 4 9]
✅ Generic Map()
mimics functional programming behavior.
⚙️ Generics vs Interface{}
Feature | interface{} | Generics |
---|---|---|
Type Safety | ❌ Requires type assertion | ✅ Compile-time type check |
Performance | ⚠️ Reflection-based, slower | ✅ No runtime overhead |
Flexibility | ✅ Works with any type | ✅ Works with constrained types |
Error Detection | ❌ Runtime | ✅ Compile-time |
🧠 Best Practices
Best Practice | Why It Matters |
---|---|
✅ Use generics to eliminate duplication | Clean, reusable, DRY code |
✅ Keep constraints narrow | Improves readability and correctness |
❌ Avoid over-generalizing | Can reduce clarity and maintainability |
✅ Use any only when truly generic | Prevents overuse of unconstrained types |
📌 Summary – Recap & Next Steps
Generics bring type-safe, reusable abstractions to Go. They make it easier to build flexible libraries, algorithms, and data structures without sacrificing performance or type safety.
🔍 Key Takeaways:
- Generics are defined using
[T any]
,[K comparable]
, etc. - Use constraints to control valid types
- Applicable to functions, structs, maps, slices, and more
- Compile-time safety with zero runtime cost
⚙️ Next: Dive into Type Sets, Generic Interfaces, or build your own Generic Stack or Queue in Go.
❓ FAQs – Go Generics
❓ What version of Go supports generics?
✅ Generics were introduced in Go 1.18.
❓ What is T any
in generics?
✅ T
is a type parameter and any
means it can be any type (alias for interface{}
).
❓ Can I have multiple type parameters?
✅ Yes. Use [T, U any]
for functions or types that take more than one type.
❓ Are generics slower than regular Go code?
✅ No. Generics are compiled to concrete types, so performance is comparable.
❓ How are generics different from interface{}
?
✅ Generics are type-safe at compile time, whereas interface{}
requires type assertions at runtime.
Share Now :