How does struct and array equality work? What is surprising about NaN as a map key?
Question 26HardGo 1.22 to 1.25
Two struct values are equal when every field is equal, compared in the order the fields are declared. Unexported fields are compared too. Blank (_) fields are skipped. Padding bytes are never compared. Arrays are equal when every element is equal. Both are compared by value, which is why a [32]byte hash or a small struct makes a good map key.
type Point struct{ X, Y int }
fmt.Println(Point{1, 2} == Point{1, 2}) // true
fmt.Println([2]int{1, 2} == [2]int{1, 2}) // true
// Anonymous struct types with the same fields are assignable and comparable:
p := struct{ X, Y int }{1, 2}
fmt.Println(p == Point{1, 2}) // true
// NaN is never equal to itself:
nan := math.NaN()
fmt.Println(nan == nan) // false
m := map[float64]int{}
m[nan] = 1
m[nan] = 2
fmt.Println(len(m)) // 2: two entries that can never be looked up
_, ok := m[nan]
fmt.Println(ok) // false
delete(m, nan) // removes nothing
clear(m) // Go 1.21: the only way to remove NaN keys
fmt.Println(len(m)) // 0
Gotchas:
- A struct with a pointer field compares the addresses, not the values they point to.
- A struct with an interface field can panic on
==if the interface holds a slice. - Adding a slice or map field to a struct used as a map key breaks compilation. Adding a
_ [0]func()field is a known trick to make a struct deliberately non-comparable. +0.0 == -0.0is true, so both map to the same key.
More on Language Fundamentals & Types
- Q24How does
switchdiffer from C, and what does thisfallthroughexample print? - Q25Which types are comparable in Go, and when does comparing values panic at run time?
- Q27What does the
comparableconstraint mean in generics, and what changed in Go 1.20? - Q28Explain method sets. Why does
var s Speaker = Dog{}fail to compile? - Q29What is addressability, and why can't you call a pointer method on a map element?
- Q30How does struct embedding affect method sets and name resolution?