Unwrapping com `errors.Is`
Parte da seção Lógica & Fluxo do Journey de GO da Coddy. Lição 40 de 68.
Ao trabalhar com erros encapsulados, você precisa de uma maneira de verificar se um erro específico existe em qualquer lugar na cadeia de erros. A função errors.Is fornece exatamente essa capacidade, permitindo identificar erros sentinela mesmo quando eles foram encapsulados várias vezes.
A função errors.Is recebe dois parâmetros: o erro que você deseja examinar e o erro alvo que você está procurando. Ela retorna true se o erro alvo for encontrado em qualquer lugar na cadeia de erros:
var ErrNotFound = errors.New("not found")
wrappedErr := fmt.Errorf("failed to load user: %w", ErrNotFound)
if errors.Is(wrappedErr, ErrNotFound) {
fmt.Println("This is a not found error")
}Esta é a maneira moderna e recomendada de verificar erros específicos em Go. Ao contrário da comparação de strings ou verificações de igualdade direta, errors.Is funciona corretamente com erros encapsulados e segue a cadeia de erros para encontrar correspondências. Isso torna o seu tratamento de erros robusto e confiável, mesmo quando os erros passam por várias camadas da sua aplicação.
Desafio
FácilCrie um sistema de monitoramento de conexão com banco de dados que use errors.Is para identificar erros de conexão específicos em cadeias de erros encapsulados. Este desafio demonstra como usar errors.Is para detectar erros sentinela mesmo quando eles foram encapsulados com contexto adicional por meio de várias camadas da aplicação.
Você receberá duas entradas:
- Uma string contendo detalhes da operação do banco de dados no formato
"database_name,operation,error_type"(por exemplo,"users_db,connect,connection_timeout") - Uma string contendo o contexto da aplicação no formato
"service_name,retry_count"(por exemplo,"AuthService,3")
Sua tarefa é:
- Definir três erros sentinela usando
errors.New():ErrConnectionTimeoutcom a mensagem"connection timeout"ErrConnectionRefusedcom a mensagem"connection refused"ErrDatabaseNotFoundcom a mensagem"database not found"
- Analisar a primeira entrada dividindo-a nas vírgulas para obter o nome do banco de dados, a operação e o tipo de erro
- Analisar a segunda entrada dividindo-a nas vírgulas para obter o nome do serviço e a quantidade de tentativas
- Criar o erro original com base no tipo de erro:
- Se o tipo de erro for
"connection_timeout": usarErrConnectionTimeout - Se o tipo de erro for
"connection_refused": usarErrConnectionRefused - Se o tipo de erro for
"database_not_found": usarErrDatabaseNotFound - Para qualquer outro tipo de erro: usar
errors.New("unknown database error")
- Se o tipo de erro for
- Criar um erro encapsulado no nível do banco de dados usando
fmt.Errorfcom%w:"failed to [operation] database [database_name]: %w" - Criar um erro encapsulado no nível do serviço:
"[service_name] database operation failed after [retry_count] retries: %w" - Usar
errors.Ispara verificar se o erro final encapsulado corresponde a cada erro sentinela e exibir os resultados:"Checking for connection timeout: [true/false]""Checking for connection refused: [true/false]""Checking for database not found: [true/false]"
- Exibir a cadeia de erros:
"Error Chain:""Original: [original_error_message]""Database level: [database_level_error_message]""Service level: [service_level_error_message]"
- Exibir a análise do erro:
"Error Analysis:""Database: [database_name]""Operation: [operation]""Service: [service_name]""Retries: [retry_count]""Error type detected: [detected_error_type]", em que detected_error_type é um de "Connection Timeout", "Connection Refused", "Database Not Found" ou "Unknown Error"
Use o pacote strings para dividir as strings de entrada nas vírgulas, o pacote errors para os erros sentinela e errors.Is, e o pacote fmt para encapsulamento de erros. Este desafio demonstra como errors.Is fornece uma identificação robusta de erros que funciona corretamente com erros encapsulados, tornando-a essencial para um tratamento de erros confiável em aplicações complexas.
Experimente você mesmo
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
// Lê a entrada
var dbOperation string
var appContext string
fmt.Scanln(&dbOperation)
fmt.Scanln(&appContext)
// Faz o parsing das entradas
dbParts := strings.Split(dbOperation, ",")
databaseName := dbParts[0]
operation := dbParts[1]
errorType := dbParts[2]
contextParts := strings.Split(appContext, ",")
serviceName := contextParts[0]
retryCount := contextParts[1]
// TODO: Escreva seu código abaixo
// 1. Defina os três erros sentinela usando errors.New()
// 2. Crie o erro original baseado no tipo de erro
// 3. Crie o erro encapsulado (wrapped) de nível de banco de dados usando fmt.Errorf com %w
// 4. Crie o erro encapsulado de nível de serviço
// 5. Use errors.Is para verificar cada erro sentinela
// 6. Exiba os resultados e a cadeia de erros
}Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Lógica & Fluxo
1Fluxo de Controle Avançado
Switch com `fallthrough`Interrompendo Loops AninhadosContinuando um Loop EspecíficoA Instrução `goto`Recapitulação - Controle de Loop Avançado4Projeto: Lista de Tarefas Simples
Configuração do ProjetoAdicionando uma Tarefa7Tratamento de Erros em Profundidade
Tipos de Erro PersonalizadosWrapping de Erros com `%w`Unwrapping com `errors.Is`Unwrapping com `errors.As`Entendendo o `panic`Usando o `recover`Recapitulação - Divisão Segura2Structs e Métodos
Definindo Métodos em StructsRecebedores de ValorRecebedores de PonteiroEscolhendo RecebedoresMétodos vs FunçõesRecapitulação - Comportamento de Structs5Mapas em Profundidade
Mapas de StructsPonteiros como Valores de MapVerificando Mapas NilComparando MapasRecapitulação - Contador de Frequência de Palavras3Interfaces (O Básico)
O que é uma Interface?Definindo uma InterfaceImplementando uma InterfaceUsando Tipos de InterfaceInterface VaziaAsserções de TipoType SwitchRecapitulação - Formas e ComportamentosPratique por conta própria: Compilador de Go online