What is comparable? What changed in Go 1.20, and what does this program print?
comparable is a predeclared constraint for types that support == and !=. It covers booleans, numbers, strings, pointers, channels, arrays and structs of comparable types, and interfaces. It excludes slices, maps and functions. You need it for map keys: func Keys[K comparable, V any](m map[K]V).
Before 1.20, interface types such as any did not satisfy comparable, because comparing two interface values can panic at run time. Go 1.20 relaxed this: spec-comparable types, including interfaces and structs containing interfaces, now satisfy comparable, even though they are not strictly comparable.
func Eq[T comparable](a, b T) bool { return a == b }
func main() {
fmt.Println(Eq[any](1, 1)) // true
fmt.Println(Eq[any](1, "1")) // false (different dynamic types)
var x, y any = []int{1}, []int{1}
fmt.Println(Eq(x, y)) // panics
}
Output: true, false, then panic: runtime error: comparing uncomparable type []int. The call compiles because T = any satisfies comparable since Go 1.20.
Other gotchas: comparable cannot appear in a union, and it does not give you ordering (<). Use cmp.Ordered for that.
More on Generics
- Q115What does the
~(tilde) token mean in a constraint? What happens without it? - Q116Explain type sets. How do unions and intersections combine, and which interfaces can only be used as constraints?
- Q118Which operations can you perform on a value of type parameter type? Why can't you access a struct field even when every type in the set has it?
- Q119How does type inference work? When does it fail, and how do you order type parameters to make partial instantiation pleasant?
- Q120Can you store a generic function in a variable or pass it around without instantiating it?
- Q121How do you get and check the zero value of a type parameter
T?