Comparing Slices in Go: Optimization and Performance

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Comparing Slices in Go: How to Avoid Losing Performance and Your Sanity

\n\n

Imagine this: you're writing a high-load service in Go, and you need to compare two byte or string slices. It seems simple, right? But as always, the devil is in the details. On Stack Overflow, the question "Checking the equality of two slices" has gathered thousands of views — and for good reason. In this article, we'll break down the pitfalls that await a developer, how to write a correct comparison, and most importantly, how to do it as fast as possible.

\n\n

We won't use boring academic definitions. Instead, we'll use live examples, benchmarks, and practical tips you can apply today. Let's go!

\n\n

Why is a Simple Comparison Not Always Correct?

\n\n

In Go, a slice is not an array. It's a descriptor that contains a pointer to the underlying array, length, and capacity. Therefore, the == operator does not work for slices. Trying to write slice1 == slice2 will result in a compilation error:

\n\n
invalid operation: slice1 == slice2 (slice can only be compared to nil)
\n\n

This is not a bug, but a feature. Go developers intentionally prohibited direct slice comparison because it can lead to non-obvious consequences. For example, two slices can reference the same underlying array but have different lengths. Or vice versa — different arrays but identical data.

\n\n

So what to do? There are several approaches, and we'll look at each one.

\n\n

Method 1: Manual Loop (Basic but Reliable)

\n\n

The most obvious way is to write a function that iterates through all elements and compares them one by one. This gives you full control over the process.

\n\n
func EqualSlices(a, b []int) bool {\n    if len(a) != len(b) {\n        return false\n    }\n    for i := range a {\n        if a[i] != b[i] {\n            return false\n        }\n    }\n    return true\n}
\n\n

Pros: transparency, no dependencies, ability to customize (e.g., comparison with tolerance for floats).
\nCons: you need to write it for each data type.

\n\n

Method 2: reflect.DeepEqual (Universal but Slow)

\n\n

The standard library has a function reflect.DeepEqual that can compare any data structures — slices, maps, structs. It looks like this:

\n\n
import "reflect"\n\nfunc main() {\n    a := []int{1, 2, 3}\n    b := []int{1, 2, 3}\n    fmt.Println(reflect.DeepEqual(a, b)) // true\n}
\n\n

Seems perfect, right? But there's a catch: reflect.DeepEqual uses reflection, which is always expensive. On small slices, the difference is negligible, but if you have thousands of elements — performance will drop significantly.

\n\n

Important: reflect.DeepEqual also considers nil and an empty slice as different! This can be surprising:

\n\n
var a []int = nil\nb := []int{}\nfmt.Println(reflect.DeepEqual(a, b)) // false!
\n\n

Method 3: bytes.Equal for Byte Slices (Optimal)

\n\n

If you're working with []byte, the best choice is the bytes.Equal function from the standard library. It's written in assembly and is highly optimized:

\n\n
import "bytes"\n\nfunc main() {\n    a := []byte("hello")\n    b := []byte("hello")\n    fmt.Println(bytes.Equal(a, b)) // true\n}
\n\n

This is the fastest way for byte slices. Internally, it uses memcmp at the C level, providing a huge performance boost.

\n\n

Method 4: Comparison Using crypto/subtle (For Cryptography)

\n\n

The crypto/subtle package deserves a special mention. Its ConstantTimeCompare function is designed for comparing secret data (passwords, tokens) without time leakage. A regular comparison can be vulnerable to timing attacks:

\n\n
import "crypto/subtle"\n\nfunc                

Blogs

Book Recommendations