Comparación de struct, slice y map en Go: de principiante a experto
\n\n¿Alguna vez ha escrito código en Go que debía comparar dos estructuras, dos slices o dos maps, y recibió un error de compilación: invalid operation: cannot compare? Si es así, no está solo. Esta es una de las preguntas más populares en Stack Overflow (¡decenas de miles de visitas!), que desconcierta incluso a desarrolladores experimentados que migraron a Go desde otros lenguajes.
En este artículo, analizaremos de una vez por todas cómo comparar correctamente tipos de datos compuestos en Go. Aprenderá por qué == no siempre funciona, cuándo usar reflect.DeepEqual y cómo escribir su propia función de comparación eficiente. ¡Comencemos!
¿Por qué la comparación simple con == no siempre funciona?
\n\nEn Go, el operador == solo funciona para tipos comparables (comparable). Estos incluyen:
- \n
- Tipos básicos:
int,string,bool,float, etc. \n - Punteros (
pointer) \n - Canales (
channel) \n - Interfaces (si los tipos dinámicos son comparables) \n
- Estructuras, cuyos campos sean todos comparables \n
- Arreglos (
array), si el elemento del arreglo es comparable \n
Sin embargo, los slices y los maps no son comparables directamente con ==. Intentar hacerlo generará un error de compilación. ¿Por qué? Porque un slice es un tipo de referencia que incluye un puntero a los datos, la longitud y la capacidad. Dos slices pueden ser "iguales" en contenido, pero tener estructuras internas diferentes.
package main\n\nimport "fmt"\n\nfunc main() {\n a := []int{1, 2, 3}\n b := []int{1, 2, 3}\n \n // ¡Error de compilación!\n // fmt.Println(a == b) \n \n fmt.Println("Solo funciona con nil:")\n fmt.Println(a == nil) // false\n fmt.Println(b == nil) // false\n}\n\nLo mismo aplica para los maps. Lo único que puede verificar con == es la igualdad con nil.
Comparación de estructuras (struct) en Go
\n\nLa comparación de estructuras es el caso más engañoso. Si todos los campos de su estructura son de tipos comparables, entonces == funciona.
type Person struct {\n Name string\n Age int\n}\n\nfunc main() {\n p1 := Person{"Alice", 30}\n p2 := Person{"Alice", 30}\n \n fmt.Println(p1 == p2) // true — ¡funciona!\n}\n\nPero el problema surge cuando la estructura contiene un slice o un map:
\n\ntype PersonWithHobbies struct {\n Name string\n Age int\n Hobbies []string // slice — tipo no comparable\n}\n\nfunc main() {\n p1 := PersonWithHobbies{"Bob", 25, []string{"reading", "coding"}}\n p2 := PersonWithHobbies{"Bob", 25, []string{"reading", "coding"}}\n \n // ¡Error de compilación!\n // fmt.Println(p1 == p2)\n}\n\nEn este caso, el compilador de Go mostrará un error: struct containing []string cannot be compared.
Solución n.º 1: reflect.DeepEqual — universal pero lento
\n\nLa forma más sencilla de comparar cualquier tipo complejo es usar la función reflect.DeepEqual de la biblioteca estándar de Go. Esta recorre recursivamente estructuras, slices y maps y compara su contenido.
import (\n "fmt"\n "reflect"\n)\n\nfunc main() {\n // Comparación de slices\n a := []int{1, 2, 3}\n b := []int{1, 2, 3}\n fmt.Println(reflect.DeepEqual(a, b)) // true\n \n // Comparación de maps\n m1 := map[string]int{"a": 1, "b": 2}\n m2 := map[string]int{"a": 1, "b": 2}\n fmt.Println(reflect.DeepEqual(m1, m2)) // true\n \n // Comparación de estructuras con campos no comparables\n p1 := PersonWithHobbies{"Bob", 25, []string{"reading"}}\n p2 := PersonWithHobbies{"Bob", 25, []string{"reading"}}\n fmt.Println(reflect.DeepEqual(p1, p2)) // true\n}\n\n<