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.

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