Menu
Coddy logo textTech

errors.Is() y errors.As()

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 56 de 107.

Cuando los errores se envuelven, las comprobaciones de igualdad simples como err == ErrNotFound ya no funcionan. El paquete errors proporciona dos funciones para inspeccionar cadenas de errores envueltos: errors.Is() y errors.As().

errors.Is() comprueba si algún error en la cadena coincide con un error centinela específico:

import "errors"

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

func FindUser(id int) error {
    return fmt.Errorf("database lookup failed: %w", ErrNotFound)
}

err := FindUser(42)
if errors.Is(err, ErrNotFound) {
    fmt.Println("User doesn't exist")
}

Aunque el error devuelto está envuelto, errors.Is() desenvuelve la cadena y encuentra ErrNotFound en su interior.

errors.As() comprueba si algún error en la cadena coincide con un tipo específico y lo extrae:

type ValidationError struct {
    Field string
}

func (e *ValidationError) Error() string {
    return "validation failed: " + e.Field
}

err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})

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

Usa errors.Is() al comprobar errores centinela, y errors.As() cuando necesites acceder a campos de un tipo de error personalizado. Ambas funciones recorren toda la cadena de errores, lo que las hace esenciales para manejar errores envueltos adecuadamente.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de procesamiento de archivos que demuestre cómo inspeccionar adecuadamente las cadenas de errores envueltos utilizando errors.Is() y errors.As(). Crearás errores que se envuelven a medida que pasan por diferentes capas, y luego usarás estas funciones para identificar y extraer información de ellos.

Organizarás tu código en dos archivos:

  • errors.go: Define la infraestructura de errores para tu sistema de archivos.

    Crea un error centinela (sentinel error):

    • ErrPermissionDenied con el mensaje "permission denied"

    Crea un tipo de error personalizado:

    • Estructura FileError con los campos Filename (string) y Operation (string)
    • Su método Error() debe devolver: [Operation] failed on file: [Filename]

    Implementa dos funciones que simulen operaciones de archivos y envuelvan errores:

    • ReadFile(filename string) error - Si el nombre del archivo es "secret.txt", devuelve ErrPermissionDenied. Si el nombre del archivo es "missing.txt", devuelve un *FileError con Operation "read" y el Filename dado. De lo contrario, devuelve nil.
    • ProcessFile(filename string) error - Llama a ReadFile. Si se devuelve un error, envuélvelo usando fmt.Errorf con el formato "processing failed: %w". De lo contrario, devuelve nil.
  • main.go: Lee un nombre de archivo de la entrada y llama a ProcessFile. Usa errors.Is() y errors.As() para inspeccionar la cadena de errores envueltos e imprimir los mensajes apropiados según lo que encuentres dentro.

Se proporcionará la siguiente entrada:

  • Línea 1: Nombre del archivo a procesar

Maneja el resultado inspeccionando la cadena de errores:

  • Si errors.Is() encuentra ErrPermissionDenied en la cadena: imprime el mensaje de error completo, luego en una nueva línea imprime Access denied - check file permissions
  • Si errors.As() encuentra un *FileError en la cadena: imprime el mensaje de error completo, luego en una nueva línea imprime File issue: [Filename] during [Operation]
  • Si no ocurre ningún error: imprime File '[filename]' processed successfully

Por ejemplo, dado secret.txt, tu salida debería ser:

processing failed: permission denied
Access denied - check file permissions

Y dado missing.txt, tu salida debería ser:

processing failed: read failed on file: missing.txt
File issue: missing.txt during read

Y dado data.txt, tu salida debería ser:

File 'data.txt' processed successfully

Observa cómo, aunque los errores son envueltos por ProcessFile, aún puedes detectar el error centinela original con errors.Is() y extraer el tipo de error personalizado con errors.As(). Estas funciones recorren toda la cadena de errores para encontrar lo que estás buscando.

Hoja de referencia

Cuando los errores están envueltos (wrapped), utiliza errors.Is() y errors.As() del paquete errors para inspeccionar la cadena de errores.

errors.Is() comprueba si algún error en la cadena coincide con un error centinela (sentinel error):

import "errors"

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

err := fmt.Errorf("database lookup failed: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
    // ErrNotFound fue encontrado en la cadena
}

errors.As() comprueba si algún error en la cadena coincide con un tipo específico y lo extrae:

type ValidationError struct {
    Field string
}

func (e *ValidationError) Error() string {
    return "validation failed: " + e.Field
}

err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})

var ve *ValidationError
if errors.As(err, &ve) {
    // ve ahora contiene el ValidationError de la cadena
    fmt.Println(ve.Field)
}

Utiliza errors.Is() para errores centinela y errors.As() cuando necesites acceder a campos de tipos de error personalizados. Ambas funciones recorren toda la cadena de errores.

Pruébalo tú mismo

package main

import (
	"errors"
	"fmt"
)

func main() {
	// Leer el nombre del archivo desde la entrada
	var filename string
	fmt.Scanln(&filename)

	// Llamar a ProcessFile con el nombre del archivo
	err := ProcessFile(filename)

	// TODO: Manejar el resultado inspeccionando la cadena de errores
	// 
	// Usar errors.Is() para comprobar si ErrPermissionDenied está en la cadena:
	// - Si se encuentra: imprimir el error completo, luego "Access denied - check file permissions"
	//
	// Usar errors.As() para comprobar si un *FileError está en la cadena:
	// - Si se encuentra: imprimir el error completo, luego "File issue: [Filename] during [Operation]"
	//
	// Si no hay error: imprimir "File '[filename]' processed successfully"

	_ = errors.Is // pista: usar errors.Is()
	_ = errors.As // pista: usar errors.As()
	_ = err
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos