Tratamento de Erros (Error Handling)¶
Em Go, erros são valores. Não existe exceção (try/catch) como em outras linguagens — o tratamento de erros é explícito e feito via retorno de valores.
O tipo error¶
error é uma interface nativa de Go:
Funções que podem falhar retornam error como último valor:
import (
"fmt"
"strconv"
)
func main() {
numero, err := strconv.Atoi("123")
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Println("Número:", numero)
}
Padrão de verificação de erro¶
O padrão idiomático em Go é sempre verificar o erro logo após a chamada:
resultado, err := algumFuncao()
if err != nil {
// trata o erro
return
}
// usa o resultado com segurança
Criando erros¶
Com errors.New¶
import "errors"
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("divisão por zero não é permitida")
}
return a / b, nil
}
Com fmt.Errorf¶
Permite formatar a mensagem de erro com contexto:
import "fmt"
func buscarUsuario(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("id inválido: %d", id)
}
return "Ana", nil
}
Erros customizados¶
Você pode criar tipos de erro personalizados implementando a interface error:
type ErroValidacao struct {
Campo string
Mensagem string
}
func (e *ErroValidacao) Error() string {
return fmt.Sprintf("campo '%s': %s", e.Campo, e.Mensagem)
}
func validarIdade(idade int) error {
if idade < 0 {
return &ErroValidacao{
Campo: "idade",
Mensagem: "não pode ser negativa",
}
}
return nil
}
Verificando o tipo do erro com errors.As:
err := validarIdade(-1)
var erroVal *ErroValidacao
if errors.As(err, &erroVal) {
fmt.Println("Erro de validação no campo:", erroVal.Campo)
}
Wrapping de erros¶
Go permite encapsular erros para adicionar contexto, mantendo o erro original:
err := fmt.Errorf("falha ao processar pedido: %w", errOriginal)
// Desempacotando com errors.Unwrap
erroInterno := errors.Unwrap(err)
// Verificando o tipo interno com errors.Is
if errors.Is(err, os.ErrNotExist) {
fmt.Println("Arquivo não encontrado")
}
Panic e Recover¶
panic interrompe o fluxo normal do programa. Use apenas em situações realmente inesperadas:
recover captura um panic dentro de um defer:
func executarSeguro() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recuperado do panic:", r)
}
}()
dividir(10, 0)
}
Atenção: prefira sempre retornar
errorem vez de usarpanic. Reserve opanicpara erros de programação irrecuperáveis.
Exemplo completo¶
package main
import (
"errors"
"fmt"
)
var ErrSaldoInsuficiente = errors.New("saldo insuficiente")
type ContaBancaria struct {
Titular string
Saldo float64
}
func (c *ContaBancaria) Sacar(valor float64) error {
if valor <= 0 {
return fmt.Errorf("valor inválido para saque: %.2f", valor)
}
if valor > c.Saldo {
return fmt.Errorf("tentativa de saque de R$%.2f: %w", valor, ErrSaldoInsuficiente)
}
c.Saldo -= valor
return nil
}
func main() {
conta := ContaBancaria{Titular: "Ana", Saldo: 500.00}
err := conta.Sacar(600.00)
if err != nil {
if errors.Is(err, ErrSaldoInsuficiente) {
fmt.Println("Operação negada:", err)
} else {
fmt.Println("Erro:", err)
}
return
}
fmt.Printf("Saque realizado. Saldo restante: R$%.2f\n", conta.Saldo)
}