Error Wrapping (fmt.Errorf)
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 54 de 107.
Quando erros passam por múltiplas camadas da sua aplicação, saber onde um erro se originou torna-se crucial. A função fmt.Errorf do Go com o verbo %w permite que você envolva erros, preservando o erro original enquanto adiciona contexto.
O encapsulamento de erros (error wrapping) cria uma cadeia de erros. Cada camada pode adicionar informações sobre o que estava tentando fazer quando o erro ocorreu:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// process data...
return nil
}O verbo %w é especial—ele envolve o erro original dentro do novo. Isso é diferente de %v, que apenas converte o erro em uma string e perde a identidade do erro original.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Quando o LoadSettings falha, a mensagem de erro mostra a cadeia completa: "loading settings: reading config file: open settings.json: no such file or directory". Cada camada adiciona contexto, tornando a depuração muito mais fácil.
O erro encapsulado ainda contém o erro original dentro dele. Na próxima lição, você aprenderá como desencapsular esses erros e verificar o que há dentro usando errors.Is() e errors.As().
Desafio
FácilVamos construir um sistema de processamento de pedidos que demonstra o encapsulamento de erros (error wrapping) em várias camadas. Você criará uma cadeia de funções onde cada camada adiciona contexto aos erros, facilitando o rastreamento exato de onde os problemas ocorrem.
Você organizará seu código em dois arquivos:
orders.go: Crie a lógica de processamento de pedidos com múltiplas camadas que encapsulam os erros à medida que eles se propagam para cima.Implemente três funções que formam uma cadeia de processamento:
ValidateItem(itemID string) error- Retorna um erro com a mensagem"item not found"se o itemID for"INVALID", caso contrário, retornanilProcessOrder(orderID, itemID string) error- ChamaValidateItem. Se retornar um erro, encapsule-o com o contexto:"processing order [orderID]: %w". Caso contrário, retornenilSubmitOrder(customerName, orderID, itemID string) error- ChamaProcessOrder. Se retornar um erro, encapsule-o com o contexto:"submitting order for [customerName]: %w". Caso contrário, retornenil
Cada camada deve usar
fmt.Errorfcom o verbo%wpara encapsular o erro da camada inferior, construindo uma cadeia de contexto.main.go: Leia os detalhes do pedido da entrada, chameSubmitOrdere exiba o resultado. Se ocorrer um erro, imprima a cadeia de erros completa. Se for bem-sucedido, imprima uma mensagem de confirmação.
As seguintes entradas serão fornecidas:
- Linha 1: Nome do cliente
- Linha 2: ID do pedido
- Linha 3: ID do item
Imprima o resultado:
- Se ocorrer um erro:
Error: [full error chain] - Se for bem-sucedido:
Order [orderID] submitted successfully for [customerName]
Por exemplo, dados Alice, ORD-123 e INVALID, sua saída deve ser:
Error: submitting order for Alice: processing order ORD-123: item not foundObserve como a mensagem de erro mostra a cadeia completa — você pode rastrear o problema desde a tentativa de envio de nível superior, passando pelo processamento do pedido, até a falha de validação real. Cada camada adicionou seu próprio contexto usando %w.
E dados Bob, ORD-456 e ITEM-001, sua saída deve ser:
Order ORD-456 submitted successfully for BobFolha de consulta
Use fmt.Errorf com o verbo %w para envolver (wrap) erros enquanto preserva o erro original:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
return nil
}O verbo %w envolve o erro original dentro do novo, ao contrário de %v, que apenas converte o erro em uma string e perde a identidade do erro original.
O empacotamento de erros cria uma cadeia onde cada camada adiciona contexto:
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}A mensagem de erro resultante mostra a cadeia completa: "loading settings: reading config file: open settings.json: no such file or directory"
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Ler o nome do cliente
scanner.Scan()
customerName := scanner.Text()
// Ler o ID do pedido
scanner.Scan()
orderID := scanner.Text()
// Ler o ID do item
scanner.Scan()
itemID := scanner.Text()
// TODO: Chamar SubmitOrder com os valores de entrada
// TODO: Se ocorrer um erro, imprimir: Error: [full error chain]
// TODO: Se for bem-sucedido, imprimir: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
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 ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa para Métodos GenéricosRecapitulação - Coleção Genérica2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool