Tratamento de Erros em Go: try/catch e muito mais de A a Z

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Tratamento de Erros em Go: filosofia e prática



Ao contrário de muitas linguagens modernas, Go (Golang) não usa a construção clássica try/catch. Em vez disso, os desenvolvedores Go adotam a abordagem de "erros como valores" (errors as values). Isso torna o código mais explícito, previsível e seguro. Neste artigo, abordaremos todos os aspectos do tratamento de erros em Go: desde padrões básicos até técnicas avançadas com defer, panic e recover.



1. Fundamentos: erros são apenas valores



Em Go, um erro é uma interface error com um único método Error() string. Qualquer função que pode falhar retorna um erro como o último valor de retorno.



package main

import ( "errors" "fmt")

// Função de divisão com retorno de errofunc divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("divisão por zero impossível") } return a / b, nil}

func main() { result, err := divide(10, 0) if err != nil { fmt.Println("Erro:", err) return } fmt.Println("Resultado:", result)}


Ponto chave: a verificação if err != nil é a forma padrão de tratar erros em Go. Não há exceções ocultas que possam "surgir" inesperadamente.



2. Criação e customização de erros



Go fornece várias formas de criar erros:



  • errors.New() — erro textual simples.
  • fmt.Errorf() — erro com formatação (suporta %w para wrapping).
  • Tipos personalizados — para transmitir contexto adicional.


Erro personalizado com dados adicionais



type DivisionError struct {    Dividend float64    Divisor  float64    Message  string}

func (e *DivisionError) Error() string { return fmt.Sprintf("%s: %f / %f", e.Message, e.Dividend, e.Divisor)}

func safeDivide(a, b float64) (float64, error) { if b == 0 { return 0, &DivisionError{ Dividend: a, Divisor: b, Message: "divisão por zero", } } return a / b, nil}

func main() { _, err := safeDivide(10, 0) if err != nil { // Type assertion para obter campos adicionais var divErr *DivisionError if errors.As(err, &divErr) { fmt.Printf("Erro com %f por %f: %s", divErr.Dividend, divErr.Divisor, divErr.Message) } }}


O uso de errors.As() permite verificar se o erro é de um tipo específico e extrair dados dele. É o equivalente ao instanceof em outras linguagens.



3. Padrões de tratamento de erros: do simples ao complexo



Em Go, existem vários padrões estabelecidos para trabalhar com erros:



3.1. Sentinel errors (erros marcadores)

São erros predefinidos que podem ser comparados com ==. Exemplo: io.EOF.



import (    "errors"    "fmt")

var ErrNotFound = errors.New("elemento não encontrado")

func findUser(id int) (string, error) { if id != 1 { return "", ErrNotFound } return "Alice", nil}

func main() { _, err := findUser(2) if errors.Is(err, ErrNotFound) { fmt.Println("Usuário não encontrado, criando novo...") }}


3.2. Wrapping de erros (encapsulamento)

A partir do Go 1.13, é possível encapsular erros usando %w em fmt.Errorf. Isso preserva a cadeia de erros.



func readConfig(path string) error {    data, err := os.ReadFile(path)    if err != nil {        return fmt.Errorf("leitura do config %s: %w", path, err)    }    // ... processamento data                

Blogs

Book Recommendations