Go tem duas formas de declarar uma variável: var, que funciona em qualquer lugar, e :=, uma forma curta que funciona dentro de funções e infere o tipo.
Toda variável tem um tipo fixo, decidido em tempo de compilação. Quando você fornece um valor, o Go infere o tipo a partir dele, então escrever o tipo também (var city string = "Lisbon") é válido, mas redundante.
var ou :=
var | := | |
|---|---|---|
| Onde | nível de pacote e dentro de funções | só dentro de funções |
| Tipo | opcional; obrigatório quando não há valor | sempre inferido |
| Valor inicial | opcional (senão, valor zero) | obrigatório |
| Redeclaração | nunca | permitida se pelo menos uma variável à esquerda for nova |
A escolha idiomática dentro de uma função:
x := valorquando você tem um valor inicial.var x Tquando você quer o valor zero, o que sinaliza "vazio de propósito":var buf bytes.Buffer,var total int.var x T = valorquando o tipo inferido não é o que você quer, por exemplovar ratio float64 = 1(um simplesratio := 1seria umint).
:= fora de uma função é erro de sintaxe:
./main.go:5:1: syntax error: non-declaration statement outside function body
Valores zero
Uma variável declarada sem valor nunca contém lixo. Ela guarda o valor zero do seu tipo:
| Tipo | Valor zero |
|---|---|
| inteiros, floats | 0 |
bool | false |
string | "" |
| ponteiros, slices, maps, channels, funções, interfaces | nil |
| arrays, structs | cada elemento ou campo com o seu valor zero |
Boas APIs em Go são projetadas para que o valor zero seja útil: um sync.Mutex zero está destravado, um bytes.Buffer zero está vazio e pronto para uso, e len, range e append funcionam em um slice nil. O caso de atenção é o map: ler um map nil devolve valores zero, mas escrever nele causa panic. Crie maps com make ou com um literal antes de gravar neles.
Declarando várias variáveis
O bloco var ( ... ) agrupa variáveis de nível de pacote relacionadas. Em uma atribuição múltipla como x, y = y, x, todas as expressões da direita são avaliadas antes de qualquer atribuição, e é por isso que a troca funciona.
:= com vários nomes não redeclara nada desde que pelo menos um nome à esquerda seja novo. Os nomes que já existem no mesmo escopo simplesmente recebem o valor. É isso que permite reutilizar err:
f, err := os.Open("a.txt") // declares f and err
g, err := os.Open("b.txt") // declares g, assigns to the existing err
Se nenhum dos nomes for novo, dá erro:
./main.go:7:4: no new variables on left side of :=
Use o = simples para atribuir a uma variável que já existe.
Variáveis sem uso são erros
Uma variável local declarada e nunca lida interrompe o build:
./main.go:9:2: declared and not used: x
Não é um aviso que dá para desligar. Apague a variável, use-a ou, durante a depuração, silencie-a com o identificador em branco: _ = x. O identificador em branco também descarta valores de retorno de que você não precisa: _, err := fmt.Println("hi").
Variáveis de nível de pacote e parâmetros de função ficam fora dessa regra.
Escopo e sombreamento (shadowing)
Uma variável existe da sua declaração até o fim do bloco que a contém ({ ... }). Um := em um bloco interno cria uma variável nova, mesmo que exista uma com o mesmo nome do lado de fora. A de fora fica sombreada, não alterada:
O programa imprime inside: 42 e depois outside: 0. O := dentro do if declarou count e err como variáveis novas no bloco interno, porque nenhuma das duas existia ali ainda. Compila sem erro e é uma fonte clássica de bugs do tipo "meu valor sumiu".
A correção é declarar err antes e atribuir com =:
var err error
count, err = strconv.Atoi(input)
go vet não reporta shadowing por padrão. O analisador separado shadow reporta, e linters como o golangci-lint podem ativá-lo.
Nomes
- Use
camelCase:maxRetries,userID. Nãomax_retries. - Mantenha siglas em uma só caixa:
userID,httpClient,parseURL, e nãouserId. - Nomes curtos para escopos curtos:
iem um laço,rpara um reader,errpara um erro. Nomes mais longos e descritivos para variáveis de nível de pacote. - Um nome com inicial maiúscula no nível de pacote (
var Timeout) é exportado para outros pacotes. Veja pacotes e imports.
Variáveis de nível de pacote
Variáveis declaradas fora de funções vivem durante todo o programa e são inicializadas antes de main rodar, na ordem das dependências. Elas são práticas para configuração e caches, e são compartilhadas por todas as goroutines. Se mais de uma goroutine escreve em uma delas, proteja-a com um mutex ou use sync/atomic; do contrário você tem uma data race.
Prefira constantes para valores que nunca mudam. Uma const não pode ser alterada por acidente e não custa nada em tempo de execução.
Perguntas frequentes
Qual a diferença entre var e := em Go?
:= declara e inicializa uma variável com tipo inferido, e só funciona dentro de funções: count := 10. var funciona em qualquer lugar (inclusive no nível de pacote), permite declarar o tipo e pode omitir o valor inicial para obter o valor zero: var count int. Dentro de funções, o Go idiomático usa := quando há valor inicial e var quando você quer o valor zero.
O que são valores zero em Go?
Toda variável declarada sem valor recebe o valor zero do seu tipo: 0 para números, false para bool, "" para string e nil para ponteiros, slices, maps, channels, funções e interfaces. Structs recebem o valor zero de cada campo. Go não tem variáveis não inicializadas.
Como corrigir "declared and not used" em Go?
Go rejeita uma variável local que recebe valor mas nunca é lida. Use a variável, apague-a ou, durante a depuração, atribua-a ao identificador em branco com _ = x. Variáveis de nível de pacote e parâmetros de função ficam fora dessa regra.
Como declarar uma variável global em Go?
Use var no nível de pacote, fora de qualquer função: var counter int. A forma curta := não é permitida ali. Um nome com inicial maiúscula (var Counter int) fica visível para outros pacotes; um com minúscula só é visível no próprio pacote. Proteja variáveis de nível de pacote com um mutex se goroutines escreverem nelas.