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.
Desafio
FácilVamos 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:
ErrPermissionDeniedcom a mensagem"permission denied"
Crie um tipo de erro personalizado:
- struct
FileErrorcom os camposFilename(string) eOperation(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", retorneErrPermissionDenied. Se filename for"missing.txt", retorne um*FileErrorcom Operation"read"e o Filename fornecido. Caso contrário, retornenil.ProcessFile(filename string) error- ChamaReadFile. Se um erro for retornado, encapsule-o usandofmt.Errorfcom o formato"processing failed: %w". Caso contrário, retornenil.
main.go: Leia um nome de arquivo da entrada e chameProcessFile. Useerrors.Is()eerrors.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()encontrarErrPermissionDeniedna cadeia: imprima a mensagem de erro completa e, em uma nova linha, imprimaAccess denied - check file permissions - Se
errors.As()encontrar um*FileErrorna cadeia: imprima a mensagem de erro completa e, em uma nova linha, imprimaFile 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 permissionsE dada a entrada missing.txt, sua saída deverá ser:
processing failed: read failed on file: missing.txt
File issue: missing.txt during readE dada a entrada data.txt, sua saída deverá ser:
File 'data.txt' processed successfullyObserve 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
}
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
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online