Go

What does the comparable constraint mean in generics, and what changed in Go 1.20?

Question 27HardGo 1.22 to 1.25

comparable is a built-in constraint that permits == and != on a type parameter, so it can be used as a map key:

func Index[T comparable](s []T, v T) int {
	for i, x := range s {
		if x == v {
			return i
		}
	}
	return -1
}

func Set[K comparable](keys ...K) map[K]struct{} {
	m := make(map[K]struct{}, len(keys))
	for _, k := range keys {
		m[k] = struct{}{}
	}
	return m
}

_ = Index([]any{1, "a"}, "a")  // Go 1.20+: compiles
_ = Index([]any{[]int{1}}, any([]int{1})) // compiles, but panics at run time

Before Go 1.20, comparable accepted only strictly comparable types, meaning types whose == can never panic. So any and other interface types couldn't satisfy it, and you couldn't write Set[any]. Go 1.20 relaxed this. Interfaces, and structs or arrays containing interfaces, now satisfy comparable, even though comparing them may panic at run time. This matches how == already behaves outside generics.

Also note:

  • comparable can only be used as a constraint, not as a regular variable type.
  • comparable does not allow <. For ordering, use cmp.Ordered (Go 1.21).
  • Only strictly comparable types implement comparable, a distinction that matters when writing your own constraints.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions