I recently saw a discussion on the Twitter Golang community about the use-cases for generics in Go. I thought it would be interesting to share my thoughts on the subject and show some examples of how I use generics in my projects.

The discussion started of with:

So Generics have been part of Go officially for a year or so now. Is anyone meaningfully using them?

I have still not found myself or my team using them, perhaps it's an "old habit die hard" sort of thing but I still kind of wish they were not part of the language. The only use I have seen of them in the wild has been in open source and I have found often it is early and unnecessary abstractions.

@MattJamesBoyle

Chunking a slice

The first example is a function that takes a slice and returns a slice of slices. The function splits the input slice into chunks of a given size.

func Chunk[T any](slice []T, chunkSize int) [][]T {
  var chunks [][]T
  for i := 0; i < len(slice); i += chunkSize {
    end := i + chunkSize
    if end > len(slice) {
      end = len(slice)
    }
    chunks = append(chunks, slice[i:end])
  }
  return chunks
}

Getting an item from a slice given an index, returning nil if it doesn't exist

The second example is a function that takes a slice and an index and returns the item at that index. If the index is out of bounds, it returns nil.

func Get[T any](s []T, i int) *T {
  if i < 0 || i >= len(s) {
    return nil
  }
  return &s[i]
}

Removing duplicates from a slice

The third example is a function that takes a slice and returns a slice with all the duplicates removed.

func Unique[T comparable](s []T) []T {
  inResult := make(map[T]bool)
  var result []T
  for _, str := range s {
    if _, ok := inResult[str]; !ok {
      inResult[str] = true
      result = append(result, str)
    }
  }
  return result
}

Get the intersection of multiple slices

The fourth example is a function that takes multiple slices and returns a slice with the intersection of all the slices.

func Intersection[T comparable](slices ...[]T) []T {
  counts := map[T]int{}
  result := []T{}

  for _, slice := range slices {
    for _, val := range slice {
      counts[val]++
    }
  }

  for val, count := range counts {
    if count == len(slices) {
      result = append(result, val)
    }
  }

  return result
}

I must admin that with the new slices and maps packages that were introduced with Go 1.21, I tend to use less generics in my own code. Without generics though, packages like slices and maps would not be possible.