Historia del tema:
Antes de Go 1.13, el error era una simple interfaz. Para proporcionar contexto adicional a los errores, a menudo se creaban tipos personalizados, pero no había un mecanismo estructurado para la anidación, como en Java moderno o C#. Con la llegada de Go 1.13, se introdujo un método estándar para la envoltura (wrapping) de errores a través de la función fmt.Errorf y la detección de la causa raíz (errors.Is/errors.As).
Problema:
En aplicaciones complejas, donde un error puede ocurrir en diferentes niveles de la pila, es importante no perder el contexto al devolver un error desde las profundidades del código. De lo contrario, la depuración se vuelve difícil y no se puede entender dónde ocurrió la falla en la cadena.
Solución:
Go propone envolver errores en nuevos objetos de error que contienen la causa. Para esto se utiliza %w en fmt.Errorf, y para verificar las causas subyacentes se utilizan errors.Is o errors.As del paquete errors.
Ejemplo de código:
import ( "errors" "fmt" ) var ErrNotFound = errors.New("no encontrado") func getData() error { return fmt.Errorf("base de datos de servicio: %w", ErrNotFound) } func main() { err := getData() if errors.Is(err, ErrNotFound) { fmt.Println("Causa detectada: no encontrado") } else { fmt.Println("Otro error:", err) } }
Características clave:
%w para anidar errores a través de fmt.Errorf.errors.Is y errors.As.¿Qué operador de formato se necesita para envolver errores a través de fmt.Errorf?
Es correcto usar %w, no %v — solo %w proporciona soporte para desempaquetar.
Ejemplo de código:
fmt.Errorf("error: %w", err)
¿Se pueden crear manualmente cadenas de errores sin fmt.Errorf y aún así detectarlas a través de errors.Is?
No, es necesario implementar la interfaz Unwrap, de lo contrario las funciones estándar no desenredan la cadena.
Ejemplo de código de la interfaz:
type wrappedError struct { msg string err error } func (w wrappedError) Error() string { return w.msg + ": " + w.err.Error() } func (w wrappedError) Unwrap() error { return w.err }
¿Devuelven los errores valores nil después de la envoltura, si el error anidado era nil?
No, si el error anidado es nil, el error envuelto también será nil solo con un uso correcto. Pero si se crea manualmente una estructura envoltora donde el campo de error es nil, no será nil. Esto a menudo causa confusión al verificar.
%w, sino solo %v — se pierde la capacidad de analizar la cadena.Unwrap() para sus estructuras de error.errors.Is/As, sino solo comparar el error directamente.En la aplicación simplemente se devolvía un nuevo error con un mensaje en cada nivel:
return errors.New("error al escribir en la base de datos")
Pros:
Contras:
Se utilizó la envoltura de errores con %w y el análisis a través de errors.Is:
if errors.Is(err, ErrNotFound) { return fmt.Errorf("error en el nivel del servicio: %w", err) }
Pros:
Contras: