Comparing int, string, or bool values in Go is straightforward with == and !=. Readers often need to compare two structs for struct equality, compare two slices where index order matters (the usual golang slice compare case), or compare maps by keys and values without caring about iteration order. This guide walks through the options that match real codebases: the == operator where the language allows it, reflect.DeepEqual, the standard slices and maps helpers (Go 1.21 onward), github.com/google/go-cmp/cmp for tests, and hand-written comparisons when you need explicit rules.
Tested with Go 1.24 on Linux;
slicesandmapsexamples require Go 1.21 or newer.
Compare structs, slices, and maps in Go
Struct equality with ==
If you need Go compare structs behavior for a plain data struct whose fields are all comparable, == is often the clearest choice: it is fast, predictable, and typed. A struct is comparable only if every field type is comparable (for example no []T, map[K]V, or func fields, and no nested non-comparable structs).
Run the example: the first pair matches on every field; the second differs on Age.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{"Bob", 20}
p2 := Person{"Bob", 20}
p3 := Person{"Bob", 40}
fmt.Println("p1 == p2:", p1 == p2)
fmt.Println("p1 == p3:", p1 == p3)
}You should see p1 == p2: true and p1 == p3: false.
Compare two slices: slices.Equal and reflect.DeepEqual
For golang compare slices or go compare two slices style checks, prefer slices.Equal when elements are comparable and order should match. It returns true when lengths match and each index compares equal with ==.
For byte slices, bytes.Equal is equally idiomatic.
Run the example: both slices.Equal calls should print true; changing order or an element would yield false.
package main
import (
"fmt"
"slices"
)
func main() {
a := []int{1, 2, 3}
b := []int{1, 2, 3}
c := []int{1, 3, 2}
fmt.Println("slices.Equal(a, b):", slices.Equal(a, b))
fmt.Println("slices.Equal(a, c):", slices.Equal(a, c))
}reflect.DeepEqual also compares slice elements, but remember it treats a nil slice and a non-nil empty slice as not deeply equal (for example []int(nil) vs []int{}), which is a common source of bugs if you expected them to match. Use DeepEqual when you already depend on reflect and accept those semantics, or for quick tests.
Compare two maps: maps.Equal and reflect.DeepEqual
For golang compare maps, maps.Equal compares the sets of keys and the value at each key using == on the values. Key order does not matter (maps are unordered); equality is about the same keys mapping to deeply equal values per the function’s definition.
Run the example: the duplicate-key maps compare equal; changing one email makes maps.Equal false.
package main
import (
"fmt"
"maps"
)
func main() {
m1 := map[string]string{"Anna": "a@x.test", "Bob": "b@x.test"}
m2 := map[string]string{"Bob": "b@x.test", "Anna": "a@x.test"}
m3 := map[string]string{"Anna": "a@x.test", "Bob": "b2@x.test"}
fmt.Println("maps.Equal(m1, m2):", maps.Equal(m1, m2))
fmt.Println("maps.Equal(m1, m3):", maps.Equal(m1, m3))
}One reflect.DeepEqual example for structs, slices, and maps
reflect.DeepEqual walks values recursively. It is widely used in tests to compare golang compare two structs shapes with nested fields, slices, and maps in one call. Read the package docs for exact rules (nil vs empty slices, time.Time, cycles, and so on).
Run the example: struct and slice pairs print true where values match; the map line shows false when a value differs.
package main
import (
"fmt"
"reflect"
)
type person struct {
name string
age int
}
func main() {
p1 := person{"Bob", 20}
p2 := person{"Bob", 20}
p3 := person{"Anna", 20}
fmt.Println("struct:", reflect.DeepEqual(p1, p2), reflect.DeepEqual(p1, p3))
s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
s3 := []int{1, 2}
fmt.Println("slice:", reflect.DeepEqual(s1, s2), reflect.DeepEqual(s1, s3))
map1 := map[string]string{"Anna": "a@x.test", "Bob": "b@x.test"}
map2 := map[string]string{"Anna": "a@x.test", "Bob": "b2@x.test"}
fmt.Println("map:", reflect.DeepEqual(map1, map2))
}go-cmp for tests: cmp.Equal and ignoring fields
For golang compare two structs in unit tests, cmp.Equal from github.com/google/go-cmp/cmp is often clearer than reflect.DeepEqual because you can attach options (for example ignore timestamps or specific fields with cmpopts.IgnoreFields). The module is intended for tests: it may panic on unsupported comparisons and is not optimized as a general-purpose production equality API.
This snippet uses modules outside the standard library; run it locally with a go.mod that requires github.com/google/go-cmp.
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{"Bob", 20}
p2 := Person{"Bob", 20}
p3 := Person{"Bob", 40}
fmt.Println("cmp p1,p2:", cmp.Equal(p1, p2))
fmt.Println("cmp p1,p3:", cmp.Equal(p1, p3))
fmt.Println("cmp p1,p3 ignore Age:", cmp.Equal(p1, p3, cmpopts.IgnoreFields(Person{}, "Age")))
}If you run it locally with go run ., you should see true, false, and true for those three lines.
To print struct values while debugging comparisons, see printing a struct in Go.
Custom comparison logic
When business rules are stricter than == or slices.Equal (for example case-insensitive strings, tolerance for floats, or ignoring certain map keys), write a small function that returns false as soon as a mismatch is found. The pattern below compares a struct that holds a slice and a map using length checks and explicit loops; it only uses the standard library so you can run it as-is.
package main
import "fmt"
type T struct {
Z []int
M map[string]string
}
func compare(a, b T) bool {
if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) {
return false
}
for i, v := range a.Z {
if b.Z[i] != v {
return false
}
}
for k, v := range a.M {
if b.M[k] != v {
return false
}
}
return true
}
func main() {
t1 := T{[]int{1, 2, 3}, map[string]string{"Anna": "a@x.test", "Bob": "b@x.test"}}
t2 := T{[]int{1, 2, 3}, map[string]string{"Bob": "b@x.test", "Anna": "a@x.test"}}
fmt.Println(compare(t1, t2))
}You should see true because keys and values align even though map literal order differs.
Summary
In Go, struct equality with == is the default when the struct type is comparable. For slice compare scenarios where order matters, use slices.Equal (or bytes.Equal for bytes) on Go 1.21+, and for map compare use maps.Equal so key/value pairs are compared without relying on iteration order. reflect.DeepEqual remains a single-call option for nested test data, provided you accept its nil-slice and other edge-case semantics. For richer test assertions and field filtering, use go-cmp. When product rules do not match any built-in definition, a short custom function keeps behavior obvious to the next reader.

