Before Go 1.18, writing a reusable function for finding a slice element required duplicate functions for
int, string, and float64, or using reflection. Generics solve this using Type Parameters.TL;DR (Quick Summary)#
- Type Parameters: Declared in square brackets:
func Map[T, U any](s []T, f func(T) U) []U. any: Alias forinterface{}. Allows any type.comparable: Built-in constraint allowing equality operations (==,!=). Required for map keys.- Underlying Type Approximation (
~T): Matches custom types whose underlying type isT(e.g.type ID intmatches~int).
1. Generics Architecture#
graph TD
GenericFunc["'Generic Function: Map[T, U any"](s []T, f func(T) U) []U"]
GenericFunc -->|Compile Time Monomorphization| IntVersion["'Instantiated: Map[int, string"]"]
GenericFunc -->|Compile Time Monomorphization| FloatVersion["'Instantiated: Map[float64, int"]"]
2. Step-by-Step Lab: Generic Data Structures & Utilities#
package main
import "fmt"
// Built-in constraint: 'comparable' allows == and != operations
func Contains[T comparable](slice []T, target T) bool {
for _, item := range slice {
if item == target {
return true
}
}
return false
}
// Custom Constraint Interface using Type Sets (|) and Approximation (~)
type Number interface {
~int | ~int64 | ~float64
}
func Sum[T Number](numbers []T) T {
var total T
for _, n := range numbers {
total += n
}
return total
}
// Custom Type matching approximation ~int
type CustomID int
func main() {
// 1. Generic Slice Contains check
intSlice := []int{10, 20, 30}
fmt.Println("Contains 20:", Contains(intSlice, 20)) // true
strSlice := []string{"apple", "banana"}
fmt.Println("Contains 'cherry':", Contains(strSlice, "cherry")) // false
// 2. Generic Math Sum with underlying type approximation ~int
customIDs := []CustomID{1, 2, 3}
fmt.Println("Sum CustomIDs:", Sum(customIDs)) // 6
floats := []float64{1.5, 2.5, 3.0}
fmt.Println("Sum Floats:", Sum(floats)) // 7.0
}3. Comparison: Interface vs Generics#
| Feature | Interface (any / interface{}) | Generics ([T any]) |
|---|---|---|
| Type Verification | Runtime (Requires Type Assertions). | Compile-Time (Strict type safety). |
| Performance | Memory allocations on heap (eface boxing). | Monomorphized code execution (No heap boxing). |
| Return Type | Returns any (Requires casting). | Preserves exact caller type T. |
4. Troubleshooting & Common Errors#
Error 1: invalid operation: item == target (operator == not defined for T)#
The Cause: Declaring a type parameter [T any] and attempting to use equality operators (==). any includes types that cannot be compared (like slices or maps).
The Fix: Change the constraint from any to comparable: func Equal[T comparable](a, b T) bool.
Summary & Next Steps#
In this episode:
- We wrote generic functions using Type Parameters
[T any]. - We used
comparablefor map/slice equality operations. - We built custom constraint sets using type unions (
|) and approximations (~).
In Episode 14: Testing, Benchmarking & Fuzzing, we will master testing.T, testing.B, and table-driven tests!

