Nesta aula, vamos explorar como declarar e usar variáveis em Go, uma das linguagens mais modernas e eficientes para desenvolvimento de sistemas. Dominar as declarações de variáveis é fundamental para escrever código claro e seguro, aproveitando o sistema de tipos estático e a inferência de tipos que Go oferece.

Go possui duas formas principais de declarar variáveis: a palavra-chave var, que permite declarações explícitas com tipo, e o operador curto :=, que infere o tipo a partir do valor atribuído. Além disso, entender os valores zero, declarações múltiplas e escopo é essencial para evitar erros comuns e escrever código idiomático.

var e :=

A forma mais básica de declarar uma variável em Go é usando a palavra-chave var. Ela pode ser usada dentro de funções ou no nível do pacote. A sintaxe geral é var nome tipo, e opcionalmente pode-se inicializar a variável com um valor. Se o valor for fornecido, o tipo pode ser omitido, pois Go o infere.

Já o operador curto := só pode ser usado dentro de funções (escopo local) e combina declaração e atribuição em um único passo. Ele infere o tipo da variável a partir do valor atribuído. É a forma mais comum e idiomática para declarar variáveis locais.

package main

import "fmt"

var globalVar = "Eu sou global" // declaração com var no nível do pacote

func main() {
    var nome string = "Alice"   // var com tipo explícito
    var idade = 30              // var com inferência de tipo
    cidade := "São Paulo"      // operador curto (apenas dentro de funções)

    fmt.Println(nome, idade, cidade)
}

Zero values

Em Go, toda variável declarada sem um valor explícito recebe automaticamente um valor zero (zero value). Isso elimina a possibilidade de variáveis não inicializadas, um problema comum em outras linguagens. O valor zero depende do tipo:

  • Tipos numéricos (int, float, etc.): 0
  • Booleanos: false
  • Strings: "" (string vazia)
  • Ponteiros, slices, maps, canais, interfaces, funções: nil
  • Structs: todos os campos com seus valores zero

Exemplo:

package main

import "fmt"

func main() {
    var i int
    var f float64
    var b bool
    var s string
    var p *int

    fmt.Printf("int: %v\n", i)        // 0
    fmt.Printf("float64: %v\n", f)    // 0
    fmt.Printf("bool: %v\n", b)       // false
    fmt.Printf("string: %q\n", s)     // ""
    fmt.Printf("pointer: %v\n", p)    // nil
}

Múltiplas declarações

Go permite declarar múltiplas variáveis em uma única linha, separando-as por vírgulas. Isso é útil para inicializar várias variáveis de uma vez, especialmente quando uma depende da outra (por exemplo, em funções que retornam múltiplos valores).

Além disso, é possível usar a sintaxe var ( ... ) para declarar várias variáveis em bloco, geralmente no nível do pacote, para melhor organização.

package main

import "fmt"

var (
    nome   = "João"
    idade  = 25
    altura = 1.75
)

func main() {
    a, b := 10, 20           // múltiplas declarações com :=
    var x, y int = 30, 40    // múltiplas declarações com var
    c, d := trocar(a, b)     // comum com retornos múltiplos

    fmt.Println(a, b, c, d)
    fmt.Println(nome, idade, altura)
}

func trocar(x, y int) (int, int) {
    return y, x
}

Escopo

O escopo de uma variável define onde ela pode ser acessada. Em Go, o escopo é determinado pelo bloco onde a variável é declarada. Um bloco é delimitado por chaves {} (por exemplo, corpo de função, if, for, switch).

Variáveis declaradas no nível do pacote (fora de qualquer função) são acessíveis em todo o pacote. Variáveis declaradas dentro de uma função são locais à função, e variáveis declaradas dentro de blocos internos (como if ou for) são acessíveis apenas naquele bloco e sub-blocos.

É importante lembrar que variáveis declaradas com := só existem dentro do bloco onde foram declaradas. Além disso, Go permite sombreamento (shadowing): uma variável em um escopo interno pode ter o mesmo nome que uma variável em escopo externo, ocultando-a temporariamente.

package main

import "fmt"

var x = "global"

func main() {
    fmt.Println(x) // global

    if true {
        x := "local" // sombreamento (shadowing)
        fmt.Println(x) // local
    }

    fmt.Println(x) // global (a variável local não afetou a global)
}

Boas práticas

Prefira usar := para declarações locais, pois é mais conciso. Use var para declarações no nível do pacote e quando precisar declarar uma variável sem inicializá-la (para que receba o valor zero). Evite sombreamento desnecessário, pois pode causar confusão. Agrupe declarações de variáveis relacionadas em blocos var() para melhor legibilidade.

Referências

Exercícios

  1. Declare uma variável nome do tipo string com valor "Maria" usando var e, em seguida, declare uma variável idade do tipo int com valor 28 usando :=. Imprima ambas.

    ✓ Resposta:
    package main
    
    import "fmt"
    
    func main() {
        var nome string = "Maria"
        idade := 28
        fmt.Println(nome, idade)
    }
    
  2. Qual é o valor zero de um slice? E de um ponteiro? Escreva um programa que declare um slice e um ponteiro sem inicializá-los e imprima seus valores.

    ✓ Resposta:

    O valor zero de um slice é nil, e de um ponteiro também é nil.

    package main
    
    import "fmt"
    
    func main() {
        var s []int
        var p *int
        fmt.Printf("slice: %v\n", s)  // slice: []
        fmt.Printf("pointer: %v\n", p) // pointer: 
    }
    
  3. Declare três variáveis x, y, z em uma única linha, com valores 10, 20 e 30, respectivamente. Use a sintaxe curta :=.

    ✓ Resposta:
    package main
    
    import "fmt"
    
    func main() {
        x, y, z := 10, 20, 30
        fmt.Println(x, y, z)
    }
    
  4. Considere o código abaixo. O que será impresso? Explique o conceito de escopo envolvido.

    package main
    
    import "fmt"
    
    var a = "global"
    
    func main() {
        fmt.Println(a)
        if true {
            a := "local"
            fmt.Println(a)
        }
        fmt.Println(a)
    }
    

    ✓ Resposta:

    Será impresso: global, local, global. Dentro do bloco if, a declaração a := "local" cria uma nova variável local que sombreia a variável global a. Fora do bloco, a variável global permanece inalterada.

  5. Declare uma variável contador no nível do pacote com valor 0. Dentro da função main, incremente-a usando contador++ e imprima o valor. Explique por que isso funciona.

    ✓ Resposta:
    package main
    
    import "fmt"
    
    var contador = 0
    
    func main() {
        contador++
        fmt.Println(contador) // 1
    }
    

    Funciona porque contador está declarada no nível do pacote, portanto é acessível dentro de main. O operador ++ apenas modifica o valor existente, sem criar uma nova variável.