Menu
Coddy logo textTech

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().

challenge icon

Desafio

Fácil

Vamos 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, retorna nil
    • ProcessOrder(orderID, itemID string) error - Chama ValidateItem. Se retornar um erro, encapsule-o com o contexto: "processing order [orderID]: %w". Caso contrário, retorne nil
    • SubmitOrder(customerName, orderID, itemID string) error - Chama ProcessOrder. Se retornar um erro, encapsule-o com o contexto: "submitting order for [customerName]: %w". Caso contrário, retorne nil

    Cada camada deve usar fmt.Errorf com o verbo %w para encapsular o erro da camada inferior, construindo uma cadeia de contexto.

  • main.go: Leia os detalhes do pedido da entrada, chame SubmitOrder e 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 found

Observe 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 Bob

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