For years, Go developers relied on interface{} (now any) and type assertions to handle different data types. While it worked, it often felt clunky and sacrificed the very type safety that makes Go so powerful. If you’ve ever found yourself writing the exact same function three times—once for int, once for int64, and once for float64—then this go generics tutorial for beginners is for you.

In my experience building automation tools at ajmani.dev, generics have drastically reduced the amount of boilerplate code I maintain. Instead of duplicating logic, I can now define the behavior once and let the compiler handle the specific types.

Core Concepts: What are Generics?

At its heart, generics allow you to write functions and data structures where the type of the data they operate on is specified as a parameter. Think of it as a “placeholder” for a type that gets filled in when the function is actually called.

Type Parameters

A type parameter is the placeholder we use. In Go, these are placed inside square brackets [] immediately after the function name. By convention, we usually use T (for Type), but you can name them whatever you like.

Type Constraints

You can’t just let T be anything if you plan on using operators like + or >. A type constraint tells the compiler, “T can be any type, as long as it supports these specific operations.” For example, the comparable constraint is built-in and allows you to use the == and != operators.

Diagram showing the difference between standard functions and generic functions in Go
Diagram showing the difference between standard functions and generic functions in Go

Getting Started: Your First Generic Function

Let’s look at a classic problem: finding the sum of a slice. Without generics, you’d need a separate function for every numeric type. Here is how we solve it using generics.

package main

import "fmt"

// Num is a custom constraint that allows any numeric type
type Num interface {
	int | int64 | float64
}

// Sum adds up elements of a slice of any numeric type
func Sum[T Num](s []T) T {
	var total T
	for _, v := range s {
		total += v
	}
	return total
}

func main() {
	ints := []int{1, 2, 3}
	floats := []float64{1.1, 2.2, 3.3}

	fmt.Println(Sum(ints))   // Output: 6
	fmt.Println(Sum(floats)) // Output: 6.6
}

As shown in the logic above, the Sum function doesn’t care if it’s handling integers or floats, as long as the type satisfies the Num interface. This is a cornerstone of modern go language features 2026 that every developer should master.

First Project: A Generic Cache Wrapper

To put this into practice, I recommend building a simple generic cache. This is a common pattern in production environments to avoid repetitive map logic. Here is a simplified version of how I implement these in my own golang project structure best practices.

package main

import "fmt"

type Cache[K comparable, V any] struct {
	store map[K]V
}

func NewCache[K comparable, V any]() *Cache[K, V] {
	return &Cache[K, V]{
		store: make(map[K]V),
	}
}

func (c *Cache[K, V]) Set(key K, value V) {
	c.store[key] = value
}

func (c *Cache[K, V]) Get(key K) (V, bool) {
	val, ok := c.store[key]
	return val, ok
}

func main() {
	// A cache where keys are strings and values are ints
	ageCache := NewCache[string, int]()
	ageCache.Set("Alice", 30)

	// A cache where keys are ints and values are strings
	idCache := NewCache[int, string]()
	idCache.Set(101, "User_A")

	fmt.Println(ageCache.Get("Alice"))
	fmt.Println(idCache.Get(101))
}

Common Mistakes When Using Generics

When I first started using generics, I fell into the trap of over-engineering. Here are the most common pitfalls to avoid:

Learning Path for Go Developers

If you’re looking to move beyond this basic tutorial, I suggest this progression:

  1. Master Constraints: Learn about the cmp.Ordered constraint in the standard library for sorting.
  2. Generic Data Structures: Try implementing a Linked List or a Binary Tree using generics.
  3. Refactor Existing Code: Find a place in your current project where you have duplicated functions for different types and refactor them.

Recommended Tools

To make your experience smoother, I recommend:

Ready to optimize your Go workflow? Start by auditing your utility packages for duplication today!