Understanding Go Generics: A Practical Guide

Go generics, introduced in Go 1.18, represent one of the most significant changes to the language since its inception. Generics enable developers to write reusable functions and data structures while preserving type safety. This guide covers the fundamentals of generics in Go and offers practical examples to help you get started.

Why Generics?

Prior to generics, Go developers often relied on interface{} or code duplication to write generic code. This approach sacrificed type safety and led to verbose, error-prone code. With generics, you can write functions and types that work with any data type, without sacrificing performance or type correctness.

Generic Functions

Here's a simple example of a generic function that returns the minimum of two values:

package main

import "fmt"

func Min[T comparable](a, b T) T {
    if a < b {
        return a
    }
    return b
}

func main() {
    fmt.Println(Min(10, 20))      // int
    fmt.Println(Min("a", "b"))    // string
}

In this example, [T comparable] specifies that T must be a type that supports comparison operators like < and >.

Generic Types

You can also create generic types, such as a generic stack:

type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() T {
    if len(s.items) == 0 {
        var zero T
        return zero
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item
}

Type Constraints

Type constraints define what operations are allowed on type parameters. The comparable constraint is built-in, but you can define your own:

type Adder[T any] interface {
    Add(a, b T) T
}

This lets you create highly flexible APIs while preserving the advantages of static typing.

Limitations and Considerations

  • Generics can increase complexity; use them judiciously.
  • Tooling and third-party support are still maturing.
  • Runtime performance is similar to non-generic code thanks to Go's monomorphization strategy.

Conclusion

Generics in Go bring powerful new capabilities to the language. By enabling reusable and type-safe abstractions, they reduce boilerplate and improve code clarity. As with any tool, they should be used where they simplify the codebase, not complicate it. With Go 1.18 and beyond, generics open new possibilities for building robust and maintainable software.

Back to top