Principales errores de desarrolladores Go principiantes: cómo evitarlos
Go (o Golang) es un lenguaje de programación compilado, estáticamente tipado, creado en las profundidades de Google. Ganó popularidad gracias a su simplicidad, rendimiento y soporte integrado para concurrencia. Sin embargo, incluso con una sintaxis tan concisa, los principiantes suelen cometer los mismos errores. En este artículo, analizaremos los 4 errores típicos principales de los desarrolladores Go principiantes y mostraremos cómo evitarlos.
1. Ignorar los errores devueltos
Uno de los errores más comunes es ignorar los errores devueltos por las funciones. En Go no hay excepciones (try-catch), y los errores son valores normales que deben manejarse explícitamente. Muchos principiantes, acostumbrados a otros lenguajes, simplemente asignan el resultado a una variable, olvidando verificar err.
Incorrecto:
package main
import ( "fmt" "os")
func main() { file, _ := os.Open("config.txt") // Ignoramos el error fmt.Println(file.Name())}Si el archivo no existe, el programa entrará en pánico (panic) o funcionará con un puntero nil, lo que provocará un comportamiento inesperado.
Correcto:
package main
import ( "fmt" "log" "os")
func main() { file, err := os.Open("config.txt") if err != nil { log.Fatalf("No se pudo abrir el archivo: %v", err) } defer file.Close() fmt.Println(file.Name())}Consejo: Verifique siempre err inmediatamente después de llamar a una función. Use log.Fatal o devuelva el error hacia arriba en la pila, pero nunca lo ignore.
2. Manejo incorrecto de gorutinas y condiciones de carrera
Las gorutinas son hilos ligeros en Go. Sin embargo, los principiantes a menudo lanzan gorutinas sin preocuparse por la sincronización del acceso a datos compartidos. Esto provoca condiciones de carrera (data race), donde múltiples gorutinas leen y escriben simultáneamente en una misma variable.
Incorrecto:
package main
import ( "fmt" "sync")
func main() { counter := 0 var wg sync.WaitGroup
for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() counter++ // ¡Condición de carrera! }() } wg.Wait() fmt.Println(counter) // El resultado puede ser cualquiera (no 1000)}Correcto (usando un mutex):
package main
import ( "fmt" "sync")
type Counter struct { mu sync.Mutex value int}
func (c *Counter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.value++}
func (c *Counter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.value}
func main() { counter := &Counter{} var wg sync.WaitGroup
for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() counter.Increment() }() } wg.Wait() fmt.Println(counter.Value()) // Siempre 1000}Consejo: Use mutexes (sync.Mutex) o canales para la sincronización. Ejecute pruebas con la bandera -race: go test -race.
3. Confusión entre slice nil y slice vacío
En Go, los slices pueden ser nil o estar vacíos. Los principiantes a menudo no entienden la diferencia, lo que provoca errores en la serialización (JSON) o al pasar slices a funciones.
- Slice nil: declarado como
var s []int. No tiene un array subyacente. - Slice vacío: creado como
s := []int{}omake([]int, 0). Tiene un array subyacente, pero su longitud es 0.
Ejemplo de confusión:
package main
import ( "e