Iteración sobre map en Go: 5 errores fatales y cómo evitarlos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Iteración sobre map en Go: cómo no pisar el rastrillo (y no perder datos)

\n\n

Si alguna vez has programado en Go, seguramente te has encontrado con map, una de las estructuras de datos más potentes y engañosas. Parece sencillo: usas range, recorres las claves, obtienes los valores. Pero el diablo, como siempre, está en los detalles. La pregunta "Iterating over all the keys of a map" en Stack Overflow ha acumulado miles de visitas, y no es casualidad.

\n\n

Hoy analizaremos los principales errores al iterar sobre map en Go que cometen incluso desarrolladores experimentados. Descubrirás por qué el orden de recorrido es una lotería, cómo trabajar de forma segura con map en gorutinas y qué hacer si el map resulta ser nil. ¡Allá vamos!

\n\n

Error n.º 1: Esperar un orden (o "¿Dónde están mis claves?")

\n\n

La trampa más común es creer que range devolverá las claves en el mismo orden en que las agregaste. En Go no es así. El orden de iteración sobre un map es fundamentalmente no determinista.

\n\n

Observa este código:

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    m := map[string]int{\n        "apple":  1,\n        "banana": 2,\n        "cherry": 3,\n    }\n\n    for i := 0; i < 3; i++ {\n        fmt.Println("Iteración", i+1)\n        for k, v := range m {\n            fmt.Printf("  %s: %d\\n", k, v)\n        }\n    }\n}\n
\n\n

Lo más probable es que cada ejecución muestre un orden diferente. Esto no es un error, sino una característica: Go aleatoriza el orden a propósito para que los desarrolladores no se acostumbren a él ni construyan lógica basada en una secuencia aleatoria.

\n\n

¿Cómo evitarlo?

\n
    \n
  • No confíes en el orden: esta es la regla principal.
  • \n
  • Si el orden es importante, usa un []string para almacenar las claves por separado y ordénalas:
  • \n
\n\n
package main\n\nimport (\n    "fmt"\n    "sort"\n)\n\nfunc main() {\n    m := map[string]int{"apple": 1, "banana": 2, "cherry": 3}\n\n    keys := make([]string, 0, len(m))\n    for k := range m {\n        keys = append(keys, k)\n    }\n    sort.Strings(keys)\n\n    for _, k := range keys {\n        fmt.Printf("%s: %d\\n", k, m[k])\n    }\n}\n
\n\n

Consejo: Si necesitas mantener el orden de inserción, considera la librería github.com/elliotchance/orderedmap o crea tu propia envoltura basada en dos estructuras (map + slice).

\n\n

Error n.º 2: Iteración con escritura simultánea (data race)

\n\n

En Go, map no es seguro para acceso concurrente. Si una gorutina lee (itera) un map mientras otra escribe en él al mismo tiempo, obtendrás un fatal error: concurrent map iteration and map write.

\n\n

Ejemplo de error fatal:

\n\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc main() {\n    m := make(map[int]int)\n\n    go func() {\n        for i := 0; i < 100; i++ {\n            m[i] = i\n        }\n    }()\n\n    go func() {\n        for k, v := range m {\n            fmt.Println(k, v)\n        }\n    }()\n\n    time.Sleep(time.Second)\n}\n
\n\n

Este código fallará con un pánico. Y no siempre: la condición de carrera puede manifestarse en la ejecución número 1000.

\n\n

¿Cómo evitarlo?

\n\n

Hay tres formas probadas:

\n\n
    \n
  1. sync.Mutex — el mutex clásico para sincronizar el acceso:
  2. \n
\n\n
package main\n\nimport (\n    "fmt"\n    "sync"\n)\n\ntype SafeMap struct {\n    mu sync.Mutex\n    m  map[int]int\n}\n\nfunc (s *SafeMap) Set(key, value int) {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    s.m[key] = value\n}\n\nfunc (s *SafeMap) Iterate(f func(key, value int) bool) {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    for k, v := range s.m {\n        if !f(k, v) {\n            break\n        }\n    }\n}\n\nfunc main() {\n    sm := SafeMap{m: make(map[int]int)}\n    sm.Set(1, 10)\n    sm.Set(2, 20)\n\n    sm.Iterate(func(k, v int) b                

Blogs

Book Recommendations