Introducción: Por qué Go es simple, pero no fácil
\n\nEl lenguaje Go (Golang) es a menudo elogiado por su simplicidad y concisión. Sin embargo, esta misma simplicidad a menudo engaña a los principiantes que vienen de lenguajes más «verbosos» como Python o Java. En este artículo hemos recopilado los principales errores de los desarrolladores principiantes en Go, desde problemas típicos con el manejo de errores hasta trampas no obvias al trabajar con gorutinas. Si estás comenzando tu camino en el mundo de Golang, esta selección te ayudará a no pisar el mismo rastrillo que muchos antes que tú.
\n\n1. Ignorar errores y manejo incorrecto
\n\nEl error más común en Go es ignorar los errores devueltos. A diferencia de las excepciones en Python o Java, Go utiliza una verificación explícita de errores a través del valor de retorno. Los principiantes a menudo escriben:
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n // ❌ ERROR: ignoramos el error\n file, _ := os.Open("file.txt")\n fmt.Println(file.Name())\n}\n\n\nAquí el símbolo _ (identificador vacío) literalmente le dice al compilador: «No me importa el error». En un proyecto real, esto provocará un pánico o un funcionamiento incorrecto. El enfoque correcto es siempre verificar el error:
package main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("file.txt")\n if err != nil {\n // ✅ Manejamos el error\n fmt.Println("Error al abrir el archivo:", err)\n return\n }\n defer file.Close()\n fmt.Println("Archivo abierto exitosamente:", file.Name())\n}\n\n\nConsejo: Nunca uses _ para errores a menos que estés 100% seguro de que el error es imposible. Es mejor escribir una línea extra if err != nil que buscar un bug por la noche antes de la fecha límite.
2. Problemas con gorutinas: condición de carrera y fugas
\n\nGo es famoso por su simplicidad al trabajar con concurrencia, pero esto también crea trampas. Los principiantes a menudo olvidan la sincronización del acceso a datos compartidos.
\n\n2.1. Condición de carrera (Data Race)
\n\nImagina que varias gorutinas escriben simultáneamente en una misma variable:
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n)\n\nfunc main() {\n counter := 0\n var wg sync.WaitGroup\n\n for i := 0; i < 1000; i++ {\n wg.Add(1)\n go func() {\n // ❌ ERROR: condición de carrera\n counter++\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Resultado impredecible\n}\n\n\nEl resultado será diferente en cada ejecución. Solución: usar un mutex u operaciones atómicas:
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n "sync/atomic"\n)\n\nfunc main() {\n var counter int64\n var wg sync.WaitGroup\n\n for i := 0; i < 1000; i++ {\n wg.Add(1)\n go func() {\n // ✅ Usamos suma atómica\n atomic.AddInt64(&counter, 1)\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Siempre 1000\n}\n\n\n2.2. Fuga de gorutinas
\n\nOtro error clásico es iniciar una gorutina y olvidarse de ella. Si una gorutina espera datos de un canal que nunca se cerrará, se quedará colgada para siempre, consumiendo memoria:
\n\npackage main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc worker(ch chan int) {\n for msg := range ch {\n fmt.Println(msg)\n }\n}\n\nfunc main() {\n ch := make(chan int)\n go worker(ch) // ❌ ERROR: la gorutina nunca terminará\n ch <- 1\n ch <- 2\n // El canal no está cerrado -> fuga\n time.Sleep(time.Second)\n}\n\n\nSolución: siempre cierra el canal cuando hayas terminado de enviar datos, o usa un contexto para la cancelación.
\n\n3. Uso incorrecto de slices nil y mapas
\n\nEn Go, n