Introdução: Por que Go é simples, mas não fácil
\n\nA linguagem Go (Golang) é frequentemente elogiada por sua simplicidade e concisão. No entanto, essa mesma simplicidade muitas vezes pega os iniciantes que vêm de linguagens mais "verbosas" como Python ou Java. Neste artigo, reunimos os principais erros de desenvolvedores Go iniciantes — desde problemas típicos com tratamento de erros até armadilhas não óbvias ao trabalhar com goroutines. Se você está apenas começando sua jornada no mundo Golang, esta seleção ajudará você a não cometer os mesmos erros que muitos antes de você.
\n\n1. Ignorar erros e tratamento incorreto
\n\nO erro mais comum em Go é ignorar os erros retornados. Ao contrário de exceções (exceptions) em Python ou Java, Go usa verificação explícita de erros através do valor retornado. Iniciantes frequentemente escrevem:
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n // ❌ ERRO: ignorando o erro\n file, _ := os.Open("file.txt")\n fmt.Println(file.Name())\n}\n\n\nAqui, o símbolo _ (identificador vazio) literalmente diz ao compilador: "Não me importo com o erro". Em um projeto real, isso levará a pânico ou funcionamento incorreto. A abordagem correta é sempre verificar o erro:
package main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("file.txt")\n if err != nil {\n // ✅ Tratando o erro\n fmt.Println("Erro ao abrir arquivo:", err)\n return\n }\n defer file.Close()\n fmt.Println("Arquivo aberto com sucesso:", file.Name())\n}\n\n\nDica: Nunca use _ para erros, a menos que tenha 100% de certeza de que o erro é impossível. É melhor escrever uma linha extra if err != nil do que procurar um bug à noite antes do prazo final.
2. Problemas com goroutines: condição de corrida e vazamentos
\n\nGo é famoso por sua simplicidade ao trabalhar com concorrência, mas isso também cria armadilhas. Iniciantes frequentemente esquecem da sincronização de acesso a dados compartilhados.
\n\n2.1. Condição de corrida (Data Race)
\n\nImagine que várias goroutines escrevem simultaneamente na mesma variável:
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n)\n\nfunc main() {\n counter := 0\n var wg sync.WaitGroup\n\n for i := 0; i < 1000; i++ {\n wg.Add(1)\n go func() {\n // ❌ ERRO: condição de corrida\n counter++\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Resultado imprevisível\n}\n\n\nO resultado será diferente a cada execução. Solução: usar mutex ou operações atômicas:
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n "sync/atomic"\n)\n\nfunc main() {\n var counter int64\n var wg sync.WaitGroup\n\n for i := 0; i < 1000; i++ {\n wg.Add(1)\n go func() {\n // ✅ Usando adição atômica\n atomic.AddInt64(&counter, 1)\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Sempre 1000\n}\n\n\n2.2. Vazamento de goroutines
\n\nOutro erro clássico é iniciar uma goroutine e esquecê-la. Se a goroutine espera dados de um canal que nunca será fechado, ela ficará suspensa para sempre, consumindo memória:
\n\npackage main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc worker(ch chan int) {\n for msg := range ch {\n fmt.Println(msg)\n }\n}\n\nfunc main() {\n ch := make(chan int)\n go worker(ch) // ❌ ERRO: a goroutine nunca terminará\n ch <- 1\n ch <- 2\n // Canal não fechado -> vazamento\n time.Sleep(time.Second)\n}\n\n\nSolução: sempre feche o canal quando terminar de enviar dados, ou use contexto para cancelamento.
\n\n3. Uso incorreto de slices e maps nil
\n\nEm Go, n