Go: panic: runtime error: index out of range y errores comunes
Cómo resolver los errores más frecuentes en Go: index out of range, nil pointer dereference, interface conversion y deadlock.
¿Por qué ocurre?
Los errores más frecuentes en Go son panics de runtime que ocurren porque: - **index out of range**: acceder a un índice que no existe en un slice o array - **nil pointer dereference**: llamar a un método o campo de un puntero nil - **interface conversion failed**: hacer type assertion incorrecta sin verificar - **concurrent map writes**: escritura concurrente en un map sin mutex - **send on closed channel**: enviar datos a un canal ya cerrado
Solución paso a paso
1. index out of range
// ❌ Panic si el slice tiene menos de 3 elementos
nombres := []string{"Ana", "Bea"}
fmt.Println(nombres[2]) // panic: runtime error: index out of range [2] with length 2// ✅ Verificar longitud antes de acceder
if len(nombres) > 2 {
fmt.Println(nombres[2])
}
// ✅ Iterar con range (nunca puede ir fuera de rango)
for i, nombre := range nombres {
fmt.Printf("%d: %s\n", i, nombre)
}
2. nil pointer dereference
// ❌ Panic si el usuario es nil
type Usuario struct {
Nombre string
}func mostrarNombre(u *Usuario) {
fmt.Println(u.Nombre) // panic si u es nil
}
// ✅ Verificar nil antes de usar el puntero
func mostrarNombre(u *Usuario) {
if u == nil {
fmt.Println("Usuario no encontrado")
return
}
fmt.Println(u.Nombre)
}
3. Type assertion con verificación
var i interface{} = "hola"// ❌ Panic si no es string
s := i.(string)
// ✅ Usar el patrón "comma ok"
s, ok := i.(string)
if !ok {
log.Println("No es un string")
return
}
fmt.Println(s)
// ✅ Usar type switch para múltiples tipos
switch v := i.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Int:", v)
default:
fmt.Printf("Tipo desconocido: %T\n", v)
}
4. Concurrent map writes — usar sync.RWMutex
import "sync"type SafeMap struct {
mu sync.RWMutex
data map[string]string
}
func (m *SafeMap) Set(key, value string) {
m.mu.Lock()
defer m.mu.Unlock()
m.data[key] = value
}
func (m *SafeMap) Get(key string) (string, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
val, ok := m.data[key]
return val, ok
}
// O usar sync.Map directamente
var m sync.Map
m.Store("clave", "valor")
val, ok := m.Load("clave")
5. Manejo de errores idiomático en Go
// Go usa errores explícitos, no excepciones
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("división por cero")
}
return a / b, nil
}// ✅ Siempre verificar el error
resultado, err := dividir(10, 0)
if err != nil {
log.Printf("Error al dividir: %v", err)
return
}
fmt.Println(resultado)
6. Recuperar de un panic (defer + recover)
func operacionSegura() (resultado string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recuperado: %v", r)
}
}() // Código que podría hacer panic
resultado = hacerAlgo()
return
}
Cómo evitarlo en el futuro
- Verifica siempre `len(slice) > índice` antes de acceder por índice - Usa `if val == nil` antes de desreferenciar punteros - Prefiere el patrón `val, ok := interface.(Tipo)` para type assertions - Usa `go race detector` para detectar race conditions: `go test -race ./...` - Maneja siempre los errores: nunca uses `_` para ignorar un error en producción
¿Quieres que una IA te ayude? Genera el prompt perfecto para tu error:
Generador de Prompts