Comparison Operators
What ==, !=, < and the rest do in Go, and which types can be compared at all.
a == b a != b a < b a <= b a > b a >= b // the result is a boolA comparison asks a question about two values and answers with a boolean, true or false. == asks whether they are equal and != whether they differ; a != b is the same question as !(a == b). <, <=, > and >= ask which value is smaller or larger. The answer can be stored in a variable or used as the condition of an if.
Ordering is for numbers and strings
Numbers order by size. Strings order byte by byte, so "apple" < "banana" is true, and every uppercase letter sorts before every lowercase one. Booleans have no order: true < false does not compile.
Equality needs a comparable type
Most values can be compared with ==. A struct is a value with named fields; two structs of the same type can be compared when every one of their field types can be, and Go checks the fields one at a time. An array is a fixed-length row of values of one type, and two arrays compare element by element.
Three kinds cannot be compared at all. A slice is a list that can grow, a map is a collection of keys with a value each, and a function value is a function stored in a variable. For those, a == b is a compile error. They may only be compared with nil, the empty value meaning nothing is there yet. To compare the contents, use slices.Equal or maps.Equal, both added in Go 1.21.
The types must line up
One side must be assignable to the type of the other. An int and an int64 will not compare; convert one of them first.
Example
package main
import "fmt"
// A point is a value with two named fields, both whole numbers.
type point struct {
X int
Y int
}
func main() {
// Numbers order by size. Strings order byte by byte.
fmt.Println(3 < 4, "apple" < "banana")
// Two structs are equal when every field is equal.
a := point{X: 1, Y: 2}
b := point{X: 1, Y: 2}
fmt.Println("same point:", a == b)
// [2]string is a fixed row of two strings. Arrays compare
// element by element.
first := [2]string{"up", "down"}
second := [2]string{"up", "left"}
fmt.Println("same row:", first == second)
// A slice is a list that can grow. It cannot be compared with
// ==, only against nil, which here means no list yet.
var names []string
fmt.Println("empty:", names == nil)
}
// Output:
// true true
// same point: true
// same row: false
// empty: trueWhen to use it
- Check whether a token sent with a request matches the one you stored.
- Decide whether a retry count has reached its limit before trying again.
- Key a map by a small struct, such as a row and column pair for a grid.
- Decide whether two configuration values loaded from different files describe the same thing.
More examples
Comparing slices and maps by content
Both functions arrived in Go 1.21 and replace the loop people used to write by hand. Use them in tests and in any check of whether two lists or two lookup tables hold the same data; note that they compare values with ==, so the element type still has to be comparable.
package main
import (
"fmt"
"maps"
"slices"
)
func main() {
left := []int{1, 2, 3}
right := []int{1, 2, 3}
// left == right does not compile. slices.Equal checks the
// lengths, then each element at the same position.
fmt.Println("slices:", slices.Equal(left, right))
ages := map[string]int{"ada": 36, "alan": 41}
same := map[string]int{"alan": 41, "ada": 36}
// maps.Equal does not care what order the keys were written in.
fmt.Println("maps:", maps.Equal(ages, same))
}
// Output:
// slices: true
// maps: trueA struct as a map key
Comparability is not only about ==: a map key must be comparable too. A small struct of numbers or strings is the clean way to key a grid position, a date pair or a tenant-and-id pair, instead of gluing the parts into one string.
package main
import "fmt"
// Both fields are whole numbers, so a cell can be compared, and
// anything that can be compared can be a map key.
type cell struct {
Row int
Col int
}
func main() {
board := map[cell]string{}
board[cell{Row: 0, Col: 0}] = "rook"
board[cell{Row: 0, Col: 1}] = "knight"
// A second cell value with the same fields is the same key.
fmt.Println(board[cell{Row: 0, Col: 1}])
}
// Output:
// knightFractional numbers need care
Reach for this whenever money, percentages or measurements are held in float64 and a test compares them. The tolerance check is the fix; the NaN line explains why a value that looks present can still fail every == you write against it.
package main
import (
"fmt"
"math"
)
func main() {
a := 0.1
b := 0.2
// Binary fractions cannot hold 0.1 exactly, so the sum is a
// hair away from 0.3.
fmt.Println("exact match:", a+b == 0.3)
// Ask instead whether the gap is small enough to ignore.
gap := math.Abs(a + b - 0.3)
fmt.Println("close enough:", gap < 1e-9)
// NaN means not a number. It is equal to nothing, itself
// included.
nan := math.NaN()
fmt.Println("nan equals itself:", nan == nan)
}
// Output:
// exact match: false
// close enough: true
// nan equals itself: false
Discussion