M01·08Erros Comuns e Como Evitar

CAPÍTULO 08

Erros Comuns e Como Evitar

Os oito erros que todo iniciante comete em Go — e como corrigi-los antes de chegar em produção.

Por Thiago Souza8 min de leituraAtualizado em 2026-05

Erro 1: Ignorar o erro retornado

go
// RUIM
dados, _ := os.ReadFile("config.json")
process(dados) // pode estar vazio se falhou!
 
// BOM
dados, err := os.ReadFile("config.json")
if err != nil {
    return fmt.Errorf("config: %w", err)
}
process(dados)

Erro 2: Capturar variável de loop em goroutine

go
// RUIM (em Go < 1.22)
for _, item := range items {
    go func() {
        fmt.Println(item) // todas imprimem o mesmo!
    }()
}
 
// BOM
for _, item := range items {
    item := item // shadowing — cria nova variável a cada iteração
    go func() {
        fmt.Println(item)
    }()
}
 
// AINDA MELHOR
for _, item := range items {
    go func(it string) {
        fmt.Println(it)
    }(item)
}
Desde Go 1.22, esse problema foi corrigido na linguagem. Mas se você lê código antigo, vai encontrar isso.

Erro 3: Esquecer de fechar channels

go
// RUIM — recebedor fica esperando para sempre
ch := make(chan int)
for i := 0; i < 5; i++ {
    ch <- i
}
for v := range ch { // deadlock!
    fmt.Println(v)
}
 
// BOM — fechar sinaliza fim para o range
ch := make(chan int, 5)
for i := 0; i < 5; i++ {
    ch <- i
}
close(ch)
for v := range ch { // sai do loop quando ch fecha
    fmt.Println(v)
}
Regra: quem envia é quem fecha. Nunca feche um channel que você só recebe.

Erro 4: Usar nil map para escrita

go
// RUIM
var m map[string]int
m["a"] = 1 // panic: assignment to entry in nil map
 
// BOM
m := make(map[string]int)
m["a"] = 1

Erro 5: Slice "compartilhando" memória inesperadamente

go
// PERIGOSO
a := []int{1, 2, 3, 4, 5}
b := a[1:3]      // b aponta pra dentro de a!
b[0] = 999       // modifica a também!
fmt.Println(a)   // [1 999 3 4 5]
 
// BOM — copie se precisar de independência
b := make([]int, 2)
copy(b, a[1:3])

Erro 6: Goroutines vazando

go
// RUIM — se ninguém receber, a goroutine fica para sempre
func vazar() {
    ch := make(chan int)
    go func() {
        ch <- 42 // bloqueia eternamente se ninguém ler
    }()
    // função retorna sem ler do channel
}
 
// BOM — use context para cancelar
func ok(ctx context.Context) {
    ch := make(chan int, 1) // ou use buffer
    go func() {
        select {
        case ch <- 42:
        case <-ctx.Done():
            return
        }
    }()
}

Erro 7: panic em código de biblioteca

panic é o throw do Go, mas só deve ser usado para erros impossíveis de recuperar (estado inválido do programa, bug grave). Em código normal, retorne erros.

go
// RUIM
func DividirNumeros(a, b int) int {
    if b == 0 {
        panic("divisão por zero!")
    }
    return a / b
}
 
// BOM
func DividirNumeros(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("divisão por zero")
    }
    return a / b, nil
}

Erro 8: Usar fmt.Println em produção

Use o pacote log (ou log/slog no Go 1.21+) que adiciona timestamps, níveis e formatação adequada:

go
import "log/slog"
 
slog.Info("usuário criado", "id", u.ID, "email", u.Email)
slog.Error("falha ao salvar", "erro", err)