Comparar valores é uma das operações mais comuns em qualquer linguagem de programação, e em Go não é diferente. Porém, a simplicidade da linguagem esconde algumas nuances importantes sobre como certos tipos podem ou não ser comparados diretamente.
Neste post, exploraremos os operadores de comparação em Go, suas limitações e alternativas.
Operadores de Comparação
De modo geral, Go oferece dois operadores para comparação direta de valores:
==: Verifica se os valores dos dois operandos são iguais.!=: Verifica se os valores dos dois operandos são diferentes.
Esses operadores funcionam para os tipos chamados de “comparáveis”, como por exemplo o boolean, int, float, tipos complexos, string, channels, arrays, structs e ponteiros.
Para os tipos numéricos (int, float e tipos complexos) também podemos utilizar >, <, <= e >=.
Tipos comparáveis
Os tipos “comparáveis” se referem a tipos de dados que podem ser comparados diretamente, através dos operadores citados acima. No exemplo abaixo, fazemos duas pequenas comparações com strings.
package main
import "fmt"
func main() {
str1 := "golang"
str2 := "golang"
str3 := "go"
fmt.Println(str1 == str2) // true
fmt.Println(str1 == str3) // false
}
Embora fazer essas comparações com tipos básicos não seja nenhuma novidade, dizer que uma struct é um tipo comparável pode soar meio estranho. Isso acontece pois muitas vezes acabamos olhando para structs como as famosas classes da orientação a objetos.
No entanto, vale lembrar, como explicamos no post “As diferenças entre structs e classes”, structs não são classes.
No exemplo abaixo, que é praticamente uma cópia anterior, podemos ver quão simples é comprar duas structs.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{Name: "Alice", Age: 30}
p2 := Person{Name: "Alice", Age: 30}
p3 := Person{Name: "Bob", Age: 25}
fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false
}
Porém, contudo, todavia… É CLARO que não pode ser tão fácil assim. Na verdade, structs só são comparáveis quando todos seus atributos são de tipos comparáveis.
Em outras palavras, se uma struct contiver algum campo que não seja comparável (como slices ou maps), o compilador emitirá um erro.
panic: runtime error: comparing uncomparable type main.X
Structs não comparáveis
Ok, mas o que eu faço para comparar duas structs que contém atributos não comparáveis (slices e maps)?
Existem basicamente duas opções para se fazer isso.
A primeira seria implementar um método “Compare”, recebendo a struct a ser comparada como parâmetro. Em sua implementação, comparar cada um dos atributos manualmente, como mostro no próximo exemplo.
package main
import "fmt"
type Data struct {
Key string
Values []int
}
func (d *Data) Compare(b *Data) bool {
if d.Key != b.Key {
return false
}
if len(d.Values) != len(b.Values) {
return false
}
for k, v := range d.Values {
if v != b.Values[k] {
return false
}
}
return true
}
func main() {
d1 := Data{Key: "A", Values: []int{1, 2, 3}}
d2 := Data{Key: "A", Values: []int{1, 2, 3}}
fmt.Println(d1.Compare(d2))
}
Embora seja uma abordagem bem verbosa, ela com certeza da conta do recado.
A segunda opção, MUITO menos verbosa, é utilizar a função DeepEqual do package reflect. Porém, assim como qualquer função/método que venha dessa package, embora facilite muito as coisas, existe um lado negativo.
Mas antes de falar desse lado, vamos ver como utilizar essa função.
Reflect.DeepEqual
A função reflect.DeepEqual , como dito anteriormente, é uma alternativa para comparar valores que não podem ser comparados diretamente, como por exemplo slices e maps.
No exemplo abaixo, reescrevemos o código anterior substituindo a implementação customizada do método “Compare”, pela utilização da função DeepEqual.
package main
import (
"fmt"
"reflect"
)
type Data struct {
Key string
Values []int
}
func main() {
d1 := Data{Key: "A", Values: []int{1, 2, 3}}
d2 := Data{Key: "A", Values: []int{1, 2, 3}}
d3 := Data{Key: "A", Values: []int{2, 1, 3}}
fmt.Println(reflect.DeepEqual(d1, d2)) // true
fmt.Println(reflect.DeepEqual(d1, d3)) // false
}
Embora poderosa, antes de utilizar reflect.DeepEqual, é importante considerar dois pontos:
- Ela faz diferenças entre slice vazio e slice nulo.
- Por usar reflection, sua performance é bem baixa quando comparada com um simples
==.
Performance
Em alguns benchmarks, reflect.DeepEqual se mostra, em média, 100x mais lenta do que quando utilizamos operadores básicos.
Se performance for um fator crítico, evite reflect.DeepEqual em caminhos de execução sensíveis. Em vez disso, implemente comparações manuais otimizadas para seu caso de uso específico.
Conclusão
Comparar valores em Go é simples, mas exige atenção a detalhes importantes, especialmente quando lidamos com tipos compostos como structs e slices.
Enquanto os operadores == e != são rápidos e intuitivos, eles não cobrem todos os casos. Para situações mais complexas, reflect.DeepEqual pode ser útil, mas com custos de performance.
Saber quando usar cada abordagem é essencial para escrever código eficiente e confiável. Agora que você entende as nuances das comparações em Go, pode aplicá-las com confiança em seus projetos.
Até a próxima!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.
Livros Recomendados
Abaixo listei alguns dos melhores livros que já li sobre GO.

