Gafes em Go: escopo de bloco
Hoje comecei a ler 100 Go Mistakes and How to Avoid Them (100 Equívocos em Go e Como Evitá-los) de Teiva Harsanyi (Manning, 2022). O autor fez um site que resume e complementa o livro, inclusive com links para os exemplos.
Como já tenho alguma experiência em Go, nem todas 100 gafes serão novidade para mim, mas quero ler o livro todo. Então vou introduzir um elemento lúdico…
Toda vez que eu sentar para ler 100 Go Mistakes eu vou sortear um dos capítulos de 2 a 12, e ler a primeira gafe daquele capítulo que eu ainda não tenha lido (o capítulo 1 é uma introdução, não tem nenhuma das 100 gafes).
O resultado do primeiro sorteio foi 2. Toda esta novela para começar pela primeira dica do livro. 😂
Dica #1: entenda o escopo de bloco das variáveis
O título original da gafe #1 é Unintended variable shadowing (ocultação não intencional de varíavel). Eu prefiro enfatizar o acerto e não o erro, então meu título é afirmativo: esta dica trata do escopo das variáveis, ou seja, quando elas são criadas e onde elas permanecem visíveis no código.
Uma diferença importante entre Go e Python é que Go tem
escopo de blocos (block scope), assim como C, Java, e até JavaScript (com a instrução let).
Isso significa que uma variável criada dentro de um bloco em
um if ou for, so é visível naquele bloco.
O primeiro exemplo é este:
var client *http.Client
if tracing {
client, err := createClientWithTracing()
if err != nil {
return err
}
log.Println(client)
} else {
client, err := createDefaultClient()
if err != nil {
return err
}
log.Println(client)
}
// fazer operações com `client`
Consegue ver o ocultamento acidental?
Na primeira linha do if e do else, o operador :=
cria novas variáveis client. Cada um destes blocos
terá sua própria client, diferente da client definida
na primeira linha do exemplo.
No final deste trecho, o valor de client sempre será nil,
As variáveis client e err dos blocos não existirão mais,
e a client do escopo externo não recebeu um valor utilizável.
Ela foi ocultada (shadowed) pelas variáveis nos blocos.
A melhor solução neste caso é usar uma atribuição simples =
em vez de := para preservar os resultados de
createClientWithTracing() ou createDefaultClient().
Mas neste caso a variável err terá que ser declarada antes,
porque Go não permite atribuir valor a uma variável que nunca
foi declarada.
Entre as soluções propostas pelo autor, eu preferi esta:
var client *http.Client
var err error
if tracing {
client, err = createClientWithTracing()
if err != nil {
return err
}
} else {
client, err = createDefaultClient()
if err != nil {
return err
}
}
// fazer operações com `client`
Gosto da ideia de escopo de bloco,
mas esta sintaxe de atribuição
x, err := f() às vezes cria esta situação
incômoda, quando uma das variáveis já existe
e a outra não.
No final do texto desta gafe, Harsanyi sugere
que mais adiante no capítulo ele mostrará como
detectar variáveis ocultadas.
Folhei o capítulo todo até achar a gafe
#16 Not using linters (não usar linters), onde ele apresenta
o comando go vet e o plug-in shadow.
Aquela dica será o tema de outro post.
Este post é parte da série Estudando Go em 2026.