Saltar al contenido principal
eLearner.app
Módulo 10 · Lección 2 de 547/50 en el curso~14 min
Lecciones del módulo (2/5)

Gestión idiomática de errores

El manejo de errores en Go es explícito: sin excepciones, sin intento/captura. Cada función que puede fallar devuelve error como último valor y la persona que llama decide. Este capítulo recopila los patrones idiomáticos para producir, propagar e inspeccionar errores de manera sólida.

La regla de oro

Go
data, err := os.ReadFile(path)
if err != nil {
    return fmt.Errorf("config %s: %w", path, err)
}

Cuatro principios:

  1. error es el último retorno.
  2. Verifique inmediatamente con if err != nil.
  3. Agregue contexto antes de propagar (con fmt.Errorf + %w).
  4. Nunca ignore un error: trátelo, regístrelo o devuélvalo.
Go
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)

Errores centinela

Un error centinela es una variable global exportada var ErrXxx = errors.New("...") con la que la persona que llama puede comparar:

Go
package store

var ErrNotFound = errors.New("not found")

func Get(key string) (string, error) {
    if !exists(key) {
        return "", ErrNotFound
    }
    // ...
}

La persona que llama usa errors.Is (NO ==), que recorre la cadena de envoltura:

Go
v, err := store.Get(k)
if errors.Is(err, store.ErrNotFound) {
    // handle 404
}

Ejemplos estándar: io.EOF, sql.ErrNoRows, os.ErrNotExist.

Errores escritos personalizados

Para transportar datos estructurados (código HTTP, campo no válido, etc.), defina un tipo:

Go
type ValidationError struct {
    Field string
    Msg   string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}

La persona que llama usa errors.As para abatir de forma segura a lo largo de la cadena:

Go
var ve *ValidationError
if errors.As(err, &ve) {
    fmt.Println("invalid field:", ve.Field)
}

errors.As recorre la cadena Unwrap() hasta que encuentra un error asignable al puntero pasado.

Envolviendo con %w

fmt.Errorf con el verbo %w produce un error que envuelve el original, conservándolo para errors.Is/errors.As:

Go
if err := db.Query(); err != nil {
    return fmt.Errorf("load users: %w", err)
}

Sin %w (usando %v o %s) obtienes una cadena más rica pero pierdes la cadena: la persona que llama ya no puede reconocer errores específicos.

Reglas para %w:

  • Sólo uno por llamada fmt.Errorf (el multi-wrap requiere errors.Join).
  • El argumento DEBE ser un error, no una cadena.
  • Añade contexto, no duplique el mensaje original.
Go
// bad: duplicates
return fmt.Errorf("error: %w", err) // "error: file not found"

// good: useful context
return fmt.Errorf("load config %s: %w", path, err)

errors.Join para múltiples errores

Desde Go 1.20, errors.Join(errs...) combina múltiples errores en uno que los mantiene todos detectables por errors.Is/As:

Go
var errs []error
for _, item := range items {
    if err := process(item); err != nil {
        errs = append(errs, fmt.Errorf("item %s: %w", item.ID, err))
    }
}
if len(errs) > 0 {
    return errors.Join(errs...)
}

Cuándo usar panic

Nunca por errores esperados. Sólo en tres casos:

  1. Error irrecuperable: una invariante violada en un lugar que "no puede suceder" (panic("unreachable")).
  2. Inicialización fallida en init()/main() de un programa independiente (falta configuración crítica → falla rápidamente).
  3. API de biblioteca que lo documenta explícitamente: por ejemplo, regexp.MustCompile (entra en pánico si el patrón no es válido; aceptable porque el patrón es una constante de tiempo de compilación).

Todo lo demás es error. No utilice panic como atajo para propagarse: rompe la composición, omite defer Close() y abruma a la persona que llama.

Ejercicios

Ejercicio#go.m10.l2.e1
Intentos: 0Cargando...

Wrappa el error de os.ReadFile con fmt.Errorf usando el verbo %w y agregando el contesto 'config:' (con la ruta).

Cargando editor...

Solución disponible después de 3 intentos

Ejercicio#go.m10.l2.e2
Intentos: 0Cargando...

Usa errores. Es para verificar si se ha errado en la cadena de envoltura corrisponde alla sentinella ErrNotFound y stampa 'manca!' en tal caso.

Cargando editor...

Solución disponible después de 3 intentos

Cuestionario#go.m10.l2.e3
Listo

¿Cuándo è idiomatico usare pánico en una librería Go?

Go
func F(x int) {
  // ??? panic(...)
}
Opciones de respuesta