Menu
Coddy logo textTech

errors.Is() e errors.As()

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 56 de 107.

Quando erros são envolvidos, verificações de igualdade simples como err == ErrNotFound não funcionam mais. O pacote errors fornece duas funções para inspecionar cadeias de erros envolvidos: errors.Is() e errors.As().

errors.Is() verifica se algum erro na cadeia corresponde a um erro sentinela 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")
}

Embora o erro retornado esteja envolto, errors.Is() desembrulha a cadeia e encontra ErrNotFound dentro.

errors.As() verifica se algum erro na cadeia corresponde a um tipo específico e o extrai:

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)
}

Use errors.Is() ao verificar erros sentinela, e errors.As() quando você precisar acessar campos de um tipo de erro personalizado. Ambas as funções percorrem toda a cadeia de erros, tornando-as essenciais para lidar com erros envolvidos adequadamente.

challenge icon

Desafio

Fácil

Vamos construir um sistema de processamento de arquivos que demonstra como inspecionar adequadamente cadeias de erros envelopados (wrapped error chains) usando errors.Is() e errors.As(). Você criará erros que são envelopados à medida que passam por diferentes camadas e, em seguida, usará essas funções para identificar e extrair informações deles.

Você organizará seu código em dois arquivos:

  • errors.go: Defina a infraestrutura de erro para o seu sistema de arquivos.

    Crie um erro sentinela:

    • ErrPermissionDenied com a mensagem "permission denied"

    Crie um tipo de erro personalizado:

    • Struct FileError com os campos Filename (string) e Operation (string)
    • Seu método Error() deve retornar: [Operation] failed on file: [Filename]

    Implemente duas funções que simulam operações de arquivo e envelopam erros:

    • ReadFile(filename string) error - Se o filename for "secret.txt", retorne ErrPermissionDenied. Se o filename for "missing.txt", retorne um *FileError com Operation "read" e o Filename fornecido. Caso contrário, retorne nil.
    • ProcessFile(filename string) error - Chama ReadFile. Se um erro for retornado, envolva-o usando fmt.Errorf com o formato "processing failed: %w". Caso contrário, retorne nil.
  • main.go: Leia um nome de arquivo da entrada e chame ProcessFile. Use errors.Is() e errors.As() para inspecionar a cadeia de erros envelopados e imprimir as mensagens apropriadas com base no que encontrar dentro dela.

A seguinte entrada será fornecida:

  • Linha 1: Nome do arquivo a ser processado

Manipule o resultado inspecionando a cadeia de erros:

  • Se errors.Is() encontrar ErrPermissionDenied na cadeia: imprima a mensagem de erro completa e, em uma nova linha, imprima Access denied - check file permissions
  • Se errors.As() encontrar um *FileError na cadeia: imprima a mensagem de erro completa e, em uma nova linha, imprima File issue: [Filename] during [Operation]
  • Se nenhum erro ocorrer: imprima File '[filename]' processed successfully

Por exemplo, dado secret.txt, sua saída deve ser:

processing failed: permission denied
Access denied - check file permissions

E dado missing.txt, sua saída deve ser:

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

E dado data.txt, sua saída deve ser:

File 'data.txt' processed successfully

Observe como, embora os erros sejam envelopados por ProcessFile, você ainda pode detectar o erro sentinela original com errors.Is() e extrair o tipo de erro personalizado com errors.As(). Essas funções percorrem toda a cadeia de erros para encontrar o que você está procurando.

Folha de consulta

Quando os erros são envolvidos (wrapped), use errors.Is() e errors.As() do pacote errors para inspecionar a cadeia de erros.

errors.Is() verifica se algum erro na cadeia corresponde a um erro sentinela (sentinel error):

import "errors"

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

err := fmt.Errorf("database lookup failed: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
    // ErrNotFound was found in the chain
}

errors.As() verifica se algum erro na cadeia corresponde a um tipo específico e o extrai:

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 now contains the ValidationError from the chain
    fmt.Println(ve.Field)
}

Use errors.Is() para erros sentinela e errors.As() quando precisar acessar campos de tipos de erro personalizados. Ambas as funções percorrem toda a cadeia de erros.

Experimente você mesmo

package main

import (
	"errors"
	"fmt"
)

func main() {
	// Lê o nome do arquivo da entrada
	var filename string
	fmt.Scanln(&filename)

	// Chama ProcessFile com o nome do arquivo
	err := ProcessFile(filename)

	// TODO: Tratar o resultado inspecionando a cadeia de erros
	// 
	// Use errors.Is() para verificar se ErrPermissionDenied está na cadeia:
	// - Se encontrado: imprima o erro completo, depois "Access denied - check file permissions"
	//
	// Use errors.As() para verificar se um *FileError está na cadeia:
	// - Se encontrado: imprima o erro completo, depois "File issue: [Filename] during [Operation]"
	//
	// Se não houver erro: imprima "File '[filename]' processed successfully"

	_ = errors.Is // dica: use errors.Is()
	_ = errors.As // dica: use errors.As()
	_ = err
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos