Go

Which types can be map keys? What happens with interface keys and NaN?

Question 63HardGo 1.22 to 1.25

Keys must be comparable: booleans, numbers, strings, pointers, channels, interfaces, and arrays or structs made only of comparable types. Slices, maps, and functions cannot be keys. Two edge cases trip people up:

// 1) Interface keys: compile-time OK, runtime panic if the dynamic type is not hashable
m := map[any]int{}
m[1] = 1
m["x"] = 2
// m[[]int{1}] = 3 // panic: runtime error: hash of unhashable type []int

// 2) NaN keys: NaN != NaN, so every insert creates a new entry
f := map[float64]int{}
nan := math.NaN()
f[nan] = 1
f[nan] = 2
_, ok := f[nan]
fmt.Println(len(f), ok) // 2 false  -- unreachable by lookup
delete(f, nan)          // no-op!
for k, v := range f { fmt.Println(k, v) } // NaN 1, NaN 2 (range still sees them)
clear(f)                // the ONLY way to remove NaN keys (Go 1.21)
fmt.Println(len(f))     // 0

// +0.0 and -0.0 are equal, so they are the same key
f[0.0] = 1
f[math.Copysign(0, -1)] = 2
fmt.Println(len(f))     // 1

Other notes:

  • Struct keys such as map[struct{X, Y int}]bool and array keys such as map[[2]int]bool work well for composite keys and are better than concatenating strings.
  • Pointer keys compare by identity, not by content.
  • In generics, the comparable constraint accepts interface types since Go 1.20, so a generic map can still panic at run time.

More on Arrays, Slices, Maps & Strings

All 37 Arrays, Slices, Maps & Strings questions