Introdução
Go (Golang) é famoso por sua simplicidade e concisão. No entanto, por trás da aparente facilidade, existem armadilhas que são especialmente traiçoeiras para desenvolvedores que já dominam a sintaxe e as construções básicas. A transição para o nível "avançado" não é sobre conhecer padrões complexos, mas sobre um entendimento profundo do funcionamento do runtime, tipos e concorrência.
Neste artigo, analisaremos os principais erros de desenvolvedores Go iniciantes que vão além de "esqueci de fechar o arquivo" ou "confundi nil com erro". Você aprenderá como evitar vazamentos de memória, manipulação incorreta de goroutines e canais, e entenderá por que nil nem sempre é nil.
Erro 1: Vazamentos de Goroutines (Goroutine Leaks)
O erro mais caro em Go é iniciar uma goroutine e esquecê-la. Desenvolvedores iniciantes frequentemente acreditam que a goroutine terminará sozinha assim que a função for executada. Mas se a goroutine ficar bloqueada em um canal que nunca receberá dados, ela ficará pendurada para sempre.
Problema: Canal esquecido sem receptor
func process(data []int) chan int { out := make(chan int) go func() { for _, v := range data { out <- v * 2 } close(out) }() return out}
func main() { ch := process([]int{1, 2, 3}) // Lê apenas um valor, o canal não será fechado fmt.Println(<-ch) // A goroutine ficará travada, tentando enviar 4 e 6 para o canal // time.Sleep(time.Second) — vazamento de memória}Solução: Sempre controle o tempo de vida da goroutine. Use context.Context para cancelamento ou canais com buffer se não tiver certeza sobre o número de leitores.
func process(ctx context.Context, data []int) <-chan int { out := make(chan int) go func() { defer close(out) for _, v := range data { select { case out <- v * 2: case <-ctx.Done(): return } } }() return out}Erro 2: Uso incorreto de select com default
A construção select com default é uma ferramenta poderosa para operações não bloqueantes. Mas iniciantes frequentemente a usam como "muleta", o que leva a um busy-loop (loop infinito com alto consumo de CPU).
Problema: Loop infinito sem bloqueio
for { select { case msg := <-ch: fmt.Println(msg) default: // Não faz nada, mas o loop não bloqueia }}Esse código consumirá a CPU, usando 100% de um núcleo. O default torna o select não bloqueante, e o loop se transforma em uma sondagem infinita.
Solução: Remova o default (assim o select aguardará) ou adicione time.Sleep ou time.Tick para reduzir a carga.
// Versão correta: sem defaultfor msg := range ch { fmt.Println(msg)}
// Ou com timeoutfor { select { case msg := <-ch: fmt.Println(msg) case <-time.After(100 * time.Millisecond): // Verificação periódica }}Erro 3: Interface nil não é nil
Esta é uma armadilha clássica do Go. Uma variável do tipo interface{} é igual a nil apenas se tanto seu tipo quanto seu valor forem nil. Se você atribuir um ponteiro nil a uma interface, a interface deixará de ser nil.
Problema: Verificação de nil através de interface
type MyError struct{}
func (e *MyError) Error() string { return "erro"}
func getError() error { var e *MyError = nil return e // Retorna um ponteiro nil, mas o tipo não é nil}
func main() { err := getError() if err != nil { fmt.Println("Há um erro!")