VOOZH about

URL: https://dev.to/oliversieto/go-do-zero-var-vs--398e

⇱ Go do zero: var vs := - DEV Community


Go tem duas formas de declarar variáveis: var e :=. Elas existem por motivos diferentes e têm regras diferentes. Saber quando cada uma se aplica evitamos erros bobos e código que não compila.

Sintaxe

var (forma longa)

var x int // tipo explícito, recebe o zero value
var x int = 5 // tipo e valor
var x = 5 // valor com tipo inferido
var x, y = 1, 2 // múltiplas variáveis de uma vez

var (
 a int
 b string = "hi"
 c = 3.14
)

O bloco var (...) é útil quando você quer agrupar declarações relacionadas, especialmente em nível de pacote.

:= (short variable declaration)

x := 5
a, b := 1, "hello"
ch := make(chan int)
v, ok := m[key]

Pense no := como um atalho para var x = expr. Ele sempre infere o tipo a partir do valor à direita, e por isso o valor é obrigatório.

Regras principais

Aspecto var :=
Onde usar Pacote ou função Só dentro de função
Tipo explícito Opcional Não tem, sempre infere
Inicializador Opcional (usa o zero value) Obrigatório
Redeclaração Não Sim, com condições

A diferença mais importante na prática: := não funciona fora de uma função. Se você tentar usar no escopo de pacote, o compilador reclama.

Redeclaração com :=

Esse é um dos pontos que mais confunde quem está começando. O := permite reusar nomes já existentes, mas só se algumas condições forem atendidas:

  1. As variáveis já existentes precisam ter sido declaradas no mesmo bloco.
  2. O tipo precisa ser o mesmo.
  3. Pelo menos uma variável do lado esquerdo precisa ser nova.
  4. Nomes não-blank precisam ser únicos no lado esquerdo.

Um padrão comum com tratamento de erro:

field, offset := nextField(s, 0)
field, offset = nextField(s, offset) // = atribui (ambas já existem)
field2, offset := nextField(s, offset) // := redeclara offset (field2 é nova)

Note que na segunda linha usamos =, porque tudo já existe. Na terceira voltamos para := porque field2 é nova, e isso "carrega" a redeclaração de offset junto.

Já o exemplo abaixo não compila:

x, y, x := 1, 2, 3 // ilegal: x aparece duas vezes

Quando usar cada um

Não existe uma regra absoluta, mas existem situações em que a escolha é praticamente automática.

Use var quando:

Está em nível de pacote (não tem escolha):

package main

var version = "1.0" // := não funciona aqui

Quer o zero value explicitamente:

var users []string // nil slice, sem alocar nada

Quer um tipo diferente do que seria inferido:

var port int32 = 8080 // com := seria int

Está declarando várias variáveis de tipos diferentes em bloco:

var (
 name string
 age int
 active bool
)

Use := quando:

Está dentro de uma função e tem um valor para inicializar.

Recebendo o retorno de uma função (esse é o uso mais idiomático):

data, err := os.ReadFile("config.yaml")

Em if, for ou switch com escopo local:

if v, ok := m["k"]; ok {
 // Faça algo
}

Aqui v e ok só existem dentro do if. Esse padrão é muito comum em Go e vale a pena memorizar.

Múltipla atribuição

Go permite atribuir várias variáveis de uma vez, onde temos algumas coisas úteis:

a, b := 1, 2
a, b = b, a // swap sem variável temporária
x, _ := getXY() // descarta o segundo retorno com _

O _ é o blank identifier. Use ele quando precisa receber um valor que a função retorna mas não vai ser usado, isso evita que o compilador reclame de variável não usada.

Pegadinhas

nil sozinho não tem tipo

var n = nil // erro: cannot infer type
var n *int = nil // ok
var n []int // ok, zero value de slice já é nil

O compilador não consegue adivinhar o tipo a partir de nil, porque nil é válido para vários tipos diferentes (ponteiros, slices, maps, channels, interfaces, funções). Você precisa indicar o tipo.

:= em escopo aninhado faz shadow

err := outer()
if true {
 err := inner() // nova variável no bloco interno
 _ = err
}
// err aqui ainda é o da função outer

A variável de fora continua intocada. Shadowing costume causar efeitos indesejáveis na execução. Então é necessário muito cuidado, ou evitar.

Variável declarada e não usada = erro de compilação

func f() {
 x := 10 // declared and not used
}

Sempre que você declara um variável, Go obriga o uso. Variáveis não usadas costumam ser sinal de bug ou código morto.

Próximo post irei falar sobre Zero Values, até lá.