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.