Generics, one of the features more frequently requested by Go developers, are making their way into the language based on a draft design that has been evolving for the last couple of years and is now entering the language change proposal process.
The current proposal is based on an initial design that leveraged the notion of type contracts to introduce parametric polymorphism into Go. That design, which InfoQ covered at the time it became known, has been eventually superseded by a new one centered around the notion of type parameters. Both designs were authored by Google engineer Ian Lance Taylor and Go co-creator Robert Griesemer, which helps understand their similarity, but the current draft has received a lot of additional input from the Go community at large and all the developers who experimented with the Go generics playground, says Taylor.
According to the new design, a type parameter is just an abstract type that is materialized at compile-time into a concrete type specified through a type argument. Type parameters are somewhat similar to ordinary, non-type parameters but they are specified through their own, square-bracketed list to distinguish them from the latter. Additionally, they can be used with types as well.
Here is an example of a generic type AGenericType
and a generic function that wraps each element of its input slice into a value of AGenericType
and returns the resulting slice:
type AGenericType[T any] struct {
val T
}
func AGenericFunction[T any](s []T) []AGenericType[T] {
var result []AGenericType[T]
for _, x := range s {
result = append(result, AGenericType[T]{x})
}
return result
}
AGenericFunction[string]([]string{"A", "B"})
AGenericFunction([]int{1, 2}) // using type inference
The syntax above should be easily understood by anyone familiar with generics in other languages.
As it happens in other languages, a type parameter can be required to satisfy a constraint. In our previous example, we declared the type parameter T
satisfies the any
constraint. This means any concrete type can be passed in place of a parameter of abstract type T
. If we wanted to restrict the types passed to the function as arguments for type parameter T
, we could specify a constraint on that type. A constraint is just an interface that the type must implement. This implies our generic function implementation can use any operations available for that interface, and only those. For example, here we are restricting allowed types to AGenericFunction2
to those that implement SpecificInterface
:
type SpecificInterface interface {
AvailableMethod() string
}
func AGenericFunction2[T SpecificInterface](s T) {
//-- can use s.AvailableMethod()
}
The any
constraint, which means no constraint at all, is defined as type any interface{}
and is equivalent to interface{}
. The reason why the Go team decided to introduce this new keyword instead of just using interface{}
, Go's empty interface which all types implement by definition, is making its semantics more stringent. On the other hand, removing the requirement to use any
to indicate a non-constrained parametric type would have made syntax parsing much more complex. The any
type is currently only available in the context of generic functions and types, although it could be made available globally at some later moment, says Griesemer.
Importantly, all proposed changes are backward compatible, so existing Go programs will continue to compile after generics will have made their way into the compiler.
There is much more to Go generics draft that you may want to be sure you understand and can't be covered here, including support for multiple parametric types, self-referring constraints, usage of type parameters and type lists, type inference for parameter and constraints, open issues, and more.
One concern about the introduction of generics into Go is the possibility they will make the language more complex. According to Robert Griesemer, this is less a concern than the correct use of generics to prevent them from being used all over the place:
We will need to establish best practices for good generics use. Generic features have their place, and sometimes they are exactly what is needed. But they are just one more tool in the tool set of Go. A move to write everything in generic fashion would be ill-conceived.
Having formally entered the Go change process, the generics draft is now being discussed in the open by the Go community. All interested parties are invited to join this process and provide their criticism and suggestions, without forgetting though that a lot of thinking and discussion have already gone into the current draft definition, so if you haven't followed generics development for Go until now you may need a little catch-up before being able to join the process.