Esse post faz parte de uma série onde pretendo compartilhar o básico essencial para desenvolver com GoLang.
As comparações lógicas utilizando os operadores ==
ou !=
não tem muito mistério. Ou seu código compara se os valores são iguais ou diferentes.
Mas existem alguns detalhes importantes de saber sobre o que você está comparando.
Interface Comparable
A primeira coisa é saber o que pode ser comparado com esses operadores, o lugar mais óbvio para saber disso é a documentação do Go: seção sobre comparadores
E outra opção é olhar a interface comparable, que foi adicionada junto com a implementação de generics do Go, todos os tipos que implementam essa interface são comparáveis.
Então basicamente todos os tipos primitivos definidos pelo Go são comparáveis, como: string
, números
(int
, float
, complex
), bool
.
Comparação de tipos complexos
Alguns tipos possuem condições para serem ou não comparáveis. Que é o caso de: struct
, slices
e channels
.
Somente são comparáveis se os elementos deles também o forem.
O interessante é que o Go valida isso a nível de compilação, te ajudando a evitar erros em runtime, por exemplo:
//Esse código compila e a comparação funciona:
func main() {
test1 := struct {
name string
}{}
test2 := struct {
name string
}{}
if test1 == test2 {
fmt.Println("Funciona")
}
}
No código acima estou criando dois structs com o atributos equivalentes e a comparação funciona.
//Esse código não compila
func main() {
test1 := struct {
name string
attributes map[string]string
}{}
test2 := struct {
name string
attributes map[string]string
}{}
if test1 == test2 {
fmt.Println("Cade?")
}
}
Já esse código vai falhar na compilação com o erro invalid operation
.
Isso acontece porque map
não é um tipo comparável em Go.
Detalhes sobre structs e map
É bastante conveniente poder comparar structs com ==
, já que esse são os tipos onde customizamos nossas modelagens.
Mas mesmo quando estiver utilizando um tipo não comparável há algumas maneiras de simplificar o código, e saber disso vai te poupar de escrever condições bem chatas.
Bom vamos imaginar que estamos trabalhando no sistema de uma escola e temos a seguinte struct não comparável:
type student struct {
ID int
name string
age int
course string
attributes map[string]string
}
A parte boa, esse struct possui um campo ID, o que facilita muito se você estiver trabalhando com dados persistidos.
Agora imagina só se estiver trabalhando com dados ainda transientes, por exemplo se estiver lendo um arquivo e precisar fazer algum tipo de processamento antes de persistir esses dados.
Você sempre vai ter a opção de comparar campo a campo, não é nada conveniente, mas funciona. Mas você pode fazer uso de composição de struct da seguinte forma:
func main() {
type identity struct {
name string
age int
course string
}
type student struct {
ID int
identity
attributes map[string]string
}
s1 := student{
identity: identity{
name: "Chuck",
age: 10,
course: "golang",
},
attributes: map[string]string{
"last_score": "10",
},
}
s2 := student{
identity: identity{
name: "Chuck",
age: 10,
course: "golang",
},
attributes: map[string]string{
"last_score": "20",
},
}
s3 := student{
identity: identity{
name: "Chuck",
age: 12,
course: "golang",
},
attributes: map[string]string{
"last_score": "20",
},
}
if s1.identity == s2.identity {
fmt.Println("Achou", s1)
}
if s1.identity != s3.identity {
fmt.Println("Não achou")
}
}
A alternativa a esse código seria fazer condições como essas:
if s1.name == s2.name && s1.age == s2.age && s1.course == s2.course {
fmt.Println("Achou", s1)
}
Você poderia extrair para uma função, para ficar menos confuso, mas mesmo assim teria que ficar mantendo essas comparações.
Faça bom uso de composição isso pode simplificar bastante o seu código!
Resumo
- Conheça a interface
comparable
- Tipos primitivos são comparáveis
- Tipos complexos podem ou não ser comparáveis
- Use a composição a seu favor.