0Pricing
Go Academy · Lección

Envoltorio y desenvoltorio de errores

Uso de %w, errors.Is y errors.As

Envoltorio y desenvoltorio de errores es una lección gratuita de Go Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Go Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Go Academy incluye 4 lecciones en total.

¿Qué es el envoltorio de errores?

El envoltorio de errores inserta un error original dentro de un error nuevo y conserva el original para su inspección mediante código, a la vez que añade contexto. Se introdujo correctamente en Go 1.13:

package main
import ("fmt"; "errors")

var ErrDB = errors.New("database unreachable")

func queryUsers() error {
    return fmt.Errorf("queryUsers: %w", ErrDB)
}

func loadProfile(id int) error {
    return fmt.Errorf("loadProfile(%d): %w", id, queryUsers())
}

func main() {
    err := loadProfile(42)
    fmt.Println(err) // loadProfile(42): queryUsers: database unreachable
}

El verbo %w

Utilice %w (no %v) en fmt.Errorf para envolver un error. El error envuelto está disponible mediante errors.Unwrap:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner error")
    outer := fmt.Errorf("outer: %w", inner)

    fmt.Println(outer)              // outer: inner error
    fmt.Println(errors.Unwrap(outer)) // inner error
    fmt.Println(errors.Is(outer, inner)) // true
}

errors.Unwrap

errors.Unwrap devuelve el siguiente error de la cadena (el error envuelto) o nil si no hay ninguno:

package main
import ("fmt"; "errors")

type Layer struct{ msg string; err error }
func (l *Layer) Error() string { return l.msg }
func (l *Layer) Unwrap() error { return l.err }

func main() {
    root := errors.New("root")
    mid  := &Layer{"mid", root}
    top  := &Layer{"top", mid}

    fmt.Println(errors.Unwrap(top))  // mid
    fmt.Println(errors.Unwrap(mid))  // root
    fmt.Println(errors.Unwrap(root)) // <nil>
}

errors.Is: recorrido de la cadena

errors.Is(err, target) desenvuelve la cadena de errores de forma recursiva hasta encontrar una coincidencia o llegar a nil:

package main
import ("fmt"; "errors")

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

func findItem(id int) error {
    return fmt.Errorf("findItem(%d): %w",
        id, fmt.Errorf("repo: %w", ErrNotFound))
}

func main() {
    err := findItem(7)
    fmt.Println(errors.Is(err, ErrNotFound)) // true — deep in chain
    fmt.Println(err)                         // full message chain
}

errors.As: recorrido de la cadena para tipos

errors.As(err, &target) recorre la cadena en busca de un error asignable al tipo de target:

package main
import ("fmt"; "errors")

type StatusError struct{ Code int }
func (e *StatusError) Error() string { return fmt.Sprintf("status %d", e.Code) }

func doRequest() error {
    return fmt.Errorf("request: %w", &StatusError{503})
}

func main() {
    err := doRequest()
    var se *StatusError
    if errors.As(err, &se) {
        fmt.Println("status code:", se.Code) // 503
    }
}

Unwrap personalizado para cadenas

Implemente Unwrap() error en su tipo de error personalizado para admitir errors.Is y errors.As:

package main
import ("fmt"; "errors")

type AppError struct {
    Op  string
    Err error
}
func (e *AppError) Error() string  { return fmt.Sprintf("%s: %v", e.Op, e.Err) }
func (e *AppError) Unwrap() error  { return e.Err }

var ErrAuth = errors.New("unauthorized")

func main() {
    err := &AppError{"login", ErrAuth}
    fmt.Println(errors.Is(err, ErrAuth)) // true
}

%v frente a %w en fmt.Errorf

La diferencia entre %v y %w:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner")

    withV := fmt.Errorf("outer: %v", inner) // string only, no wrap
    withW := fmt.Errorf("outer: %w", inner) // wraps inner

    fmt.Println(errors.Is(withV, inner)) // false — not wrapped
    fmt.Println(errors.Is(withW, inner)) // true  — wrapped
}

Envolver errores en la práctica: controlador HTTP

Envolver los errores de forma coherente facilita la depuración en sistemas de producción:

package main
import "fmt"

func dbQuery(sql string) error {
    return fmt.Errorf("db.Query(%q): connection refused", sql)
}

func getUserByID(id int) (string, error) {
    if err := dbQuery("SELECT * FROM users WHERE id=?"); err != nil {
        return "", fmt.Errorf("getUserByID(%d): %w", id, err)
    }
    return "Alice", nil
}

func handler() error {
    _, err := getUserByID(1)
    if err != nil {
        return fmt.Errorf("handler: %w", err)
    }
    return nil
}

func main() { fmt.Println(handler()) }

errors.Join: múltiples envoltorios (Go 1.20+)

errors.Join crea un error que envuelve varios errores. errors.Is los comprueba todos:

package main
import ("fmt"; "errors")

var ErrA = errors.New("err A")
var ErrB = errors.New("err B")

func main() {
    joined := errors.Join(ErrA, ErrB)
    fmt.Println(joined)
    fmt.Println(errors.Is(joined, ErrA)) // true
    fmt.Println(errors.Is(joined, ErrB)) // true
}

Prácticas recomendadas para desenvolver errores

Directrices para envolver errores:

  • Envuelva con %w para conservar el error y permitir su inspección mediante código
  • Añada el nombre de la operación como contexto: fmt.Errorf("funcName: %w", err)
  • No envuelva errores centinela si los llamadores no van a utilizar errors.Is
  • Evite envolver dos veces el mismo contexto en cada nivel
  • Implemente Unwrap() en los tipos personalizados para que participen en las cadenas

Comprobación rápida

¿Cuál es la diferencia entre utilizar %v y %w en fmt.Errorf?

Repaso: envolver y desenvolver errores

Resumen:

  • fmt.Errorf("ctx: %w", err) envuelve con contexto
  • errors.Unwrap obtiene el siguiente error de la cadena
  • errors.Is recorre la cadena para buscar una coincidencia de identidad
  • errors.As recorre la cadena para buscar una coincidencia de tipo
  • Los tipos personalizados implementan Unwrap() error
  • errors.Join envuelve varios errores (Go 1.20+)

Preguntas frecuentes

¿La lección «Envoltorio y desenvoltorio de errores» es gratis?

Sí — el texto completo de «Envoltorio y desenvoltorio de errores» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Go Academy, actualiza a CoddyKit PRO. El curso de Go Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Envoltorio y desenvoltorio de errores»?

Uso de %w, errors.Is y errors.As Practicas Go Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Go Academy?

No se requiere experiencia previa. Go Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Envoltorio y desenvoltorio de errores»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Go Academy?

Sí. Cada lección de Go Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. La interfaz error
  2. Creación de errores personalizados
  3. Envoltorio y desenvoltorio de errores
  4. panic, recover y defer
← Volver a Go Academy