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 criar um sistema de processamento de arquivos que demonstra como inspecionar corretamente cadeias de erros encapsulados usando errors.Is() e errors.As(). Você criará erros que serão encapsulados à medida que passam por diferentes camadas e, em seguida, usará essas funções para identificá-los e extrair informações deles.

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

  • errors.go: Defina a infraestrutura de erros do 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 encapsulam erros:

    • ReadFile(filename string) error - Se filename for "secret.txt", retorne ErrPermissionDenied. Se 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, encapsule-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 encapsulados e imprimir mensagens apropriadas com base no que encontrar.

A seguinte entrada será fornecida:

  • Linha 1: Nome do arquivo a ser processado

Trate 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, dada a entrada secret.txt, sua saída deverá ser:

processing failed: permission denied
Access denied - check file permissions

E dada a entrada missing.txt, sua saída deverá ser:

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

E dada a entrada data.txt, sua saída deverá ser:

File 'data.txt' processed successfully

Observe que, embora os erros sejam encapsulados 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.

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

Pratique por conta própria: Compilador de Go online