What are Generics in Go?

One of the newer and interesting features in Go (Golang) is generics, which was introduced in Go 1.18. Generics allow you to write more flexible and reusable code by defining functions, data structures, or methods that work with any data type.

Here’s a quick explanation and example of how to use generics in Go.

What are Generics?

Before Go 1.18, you had to write different versions of a function for each type or rely on interface{} types and type assertion. Generics let you define functions or types with type parameters, making your code more type-safe and eliminating boilerplate code.

Example: Generic Function

Let's create a simple function to find the minimum value between two items of any comparable type (such as int, float, or string).

package main

import (
	"fmt"
)

// Generic Min function using type parameters
func Min[T comparable](a, b T) T {
	if a < b {
		return a
	}
	return b
}

func main() {
	// Test with integers
	fmt.Println(Min(3, 5)) // Output: 3

	// Test with floats
	fmt.Println(Min(4.5, 2.3)) // Output: 2.3

	// Test with strings
	fmt.Println(Min("apple", "banana")) // Output: apple
}

Explanation:

  • [T comparable]: This defines a generic type T, and the constraint comparable ensures that the type can be compared using <, >, and == (i.e., int, float, string, etc.).
  • The Min function works with any type that satisfies the comparable constraint, allowing you to reuse this code for integers, floats, and strings without writing separate functions.

Example: Generic Data Structure

Here’s how you can use generics in a custom data structure, like a simple stack:

package main

import "fmt"

// Define a generic Stack type
type Stack[T any] struct {
	items []T
}

// Push method to add an item to the stack
func (s *Stack[T]) Push(item T) {
	s.items = append(s.items, item)
}

// Pop method to remove and return the last item
func (s *Stack[T]) Pop() T {
	if len(s.items) == 0 {
		var zero T
		return zero // return zero value if stack is empty
	}
	lastIndex := len(s.items) - 1
	item := s.items[lastIndex]
	s.items = s.items[:lastIndex]
	return item
}

func main() {
	// Create a stack of integers
	intStack := Stack[int]{}
	intStack.Push(10)
	intStack.Push(20)
	fmt.Println(intStack.Pop()) // Output: 20

	// Create a stack of strings
	stringStack := Stack[string]{}
	stringStack.Push("hello")
	stringStack.Push("world")
	fmt.Println(stringStack.Pop()) // Output: world
}

Explanation:

  • Stack[T any]: This defines a generic type T for the Stack structure. The any keyword allows T to be any type.
  • The Push and Pop methods operate on stacks containing elements of the generic type T.

Generics in Go offer a lot of flexibility, allowing developers to write more abstract, reusable, and type-safe code without losing the simplicity that Go is known for.

Subscribe to MicroGoLang

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe