Blog do Ramalho.org

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.

Tags: