#development #golang #pattern

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.


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.

 1func Chunk[T any](slice []T, chunkSize int) [][]T {
 2    var chunks [][]T
 3    for i := 0; i < len(slice); i += chunkSize {
 4        end := i + chunkSize
 5        if end > len(slice) {
 6            end = len(slice)
 7        }
 8        chunks = append(chunks, slice[i:end])
 9    }
10    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.

1func Get[T any](s []T, i int) *T {
2    if i < 0 || i >= len(s) {
3        return nil
4    }
5    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.

 1func Unique[T comparable](s []T) []T {
 2    inResult := make(map[T]bool)
 3    var result []T
 4    for _, str := range s {
 5        if _, ok := inResult[str]; !ok {
 6            inResult[str] = true
 7            result = append(result, str)
 8        }
 9    }
10    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.

 1func Intersection[T comparable](slices ...[]T) []T {
 2    counts := map[T]int{}
 3    result := []T{}
 5    for _, slice := range slices {
 6        for _, val := range slice {
 7            counts[val]++
 8        }
 9    }
11    for val, count := range counts {
12        if count == len(slices) {
13            result = append(result, val)
14        }
15    }
17    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.