Go has been surviving for years without generics. The latest major version update (Go 1.18) finally introduced them and I think they're amazing at making Go code look much cleaner! Here are some examples where I found it useful:
1 - Unmarshaling JSON strings
One of the examples that everyone has seen before in Go is unmarshaling a JSON string into a Go struct:
str := `{"page": 1, "fruits": ["apple", "peach"]}`
res := response{}
err := json.Unmarshal([]byte(str), &res)
What I don't like about this example is that you have to pass an output parameter as input (&res
). Go supports multiple output parameters so we should never have to do this.
With generics, you can implement a function that instead initializes the right type and returns it:
func unmarshal[T any](data []byte) (*T, error) {
out := new(T)
if err := json.Unmarshal(data, out); err != nil {
return nil, err
}
return out, nil
}
str := `{"page": 1, "fruits": ["apple", "peach"]}`
res, err := unmarshal[response]([]byte(str))
2 - Type safe containers
In go, if you want to implement a linked list without generics, it would look something like this:
type Node struct {
prev *Node
next *Node
value interface{}
}
type List struct {
head *Node
tail *Node
}
It's not type safe! It uses interface{} and will require casts all over the code.
Or you can also implement a very specific LinkedList for integers, or for strings, but not one that can work for any type.
With generics, you can have an abstract LinkedList that works for any type and it's still type safe:
type Node[T any] struct {
prev *Node[T]
next *Node[T]
value T
}
type List[T any] struct {
head *Node[T]
tail *Node[T]
}
3 - Slice utilities
One of the most frustrating things in Go is to iterate over a slice to perform a basic operation. For example, if you want to check if a certain string is in slice of strings, this is how you do it:
func contains(strings []string, s string) bool {
for _, s := range strings {
if s == string {
return true
}
return false
}
exists := contains([]string{"a", "b"}, "a")
However, it only works for strings! If you want to do the same thing with integers, or structs, you have to reimplement this logic everywhere.
With generics, the standard lib introduces a much needed utility that works with any slice of comparable types:
exists := slices.Contains([]string{"a", "b"}, "a")
Conclusion
I love Go. We use it a lot at Multy. I think generics were one of the missing pieces to make it a really good language, but they are controversial in the Go community. What is your opinion on them?
Top comments (0)