Carregando
Todos os artigos
Programação 16 min de leitura

Como Definir uma Variável em Go: Um Guia Visual e Prático

Aprenda variáveis em Go de forma visual: var, :=, valores zero, os tipos básicos e os seus tamanhos, conversões rígidas, estouro, constantes, strings como bytes vs. runes, os erros do compilador que você vai encontrar, saídas reais e exercícios com respostas.

nbkhope
Nesta página

Variáveis são o jeito de um programa lembrar das coisas: uma pontuação, um nome, um preço. O Go dá duas formas de criá-las, o var e o curtinho :=, e algumas regras que deixam o código Go incomumente seguro: toda variável começa com um valor sensato, os tipos nunca se misturam por acidente, e uma variável sem uso nem compila.

Neste guia você vai declarar variáveis de todos os jeitos que o Go permite, conhecer os tipos básicos e quanta memória cada um ocupa, converter entre eles e topar com as surpresas clássicas (estouro, decimais que não são exatos, strings cujo tamanho não é o que você espera), com as mensagens reais do compilador pelo caminho. Exercícios fecham o artigo.

Todas as saídas abaixo vêm de execuções reais com o Go 1.22 no Windows de 64 bits. As mensagens do compilador são em inglês; vamos traduzindo as importantes.

Declarando com var

A forma completa é a palavra-chave var, um nome e um tipo:

1package main
2
3import "fmt"
4
5func main() {
6	var idade int
7	idade = 35
8	fmt.Println(idade)
9
10	var nome string = "Ana Maria"
11	fmt.Println(nome)
12}
35
Ana Maria
A anatomia de uma declaração de variável em Go. var idade int = 35: var é a palavra-chave que declara uma variável; idade é o nome que você usa no código; int é o tipo, que decide o que a variável guarda e quanta memória ocupa, 8 bytes; e = 35 é o valor inicial. Repare na ordem: em Go o tipo vem depois do nome, o contrário do C, do C++ e do Java, então se lê da esquerda para a direita: variável idade, um int, igual a 35. A forma curta idade := 35 declara e inicializa de uma vez, deixando o Go descobrir o tipo

Repare na ordem: o tipo vem depois do nome, o contrário do C, do C++ e do Java. Lê-se da esquerda para a direita, como uma frase: "variável idade, um int". O int guarda números inteiros (sem casas decimais), como faz sentido para uma idade.

Toda variável começa com um valor zero

Em Go, uma variável declarada nunca tem lixo: ela começa com o valor zero do seu tipo.

1package main
2
3import "fmt"
4
5func main() {
6	var quantidade int
7	var preco float64
8	var nome string
9	var pronto bool
10	fmt.Printf("%v %v %q %v\n", quantidade, preco, nome, pronto)
11}
0 0 "" false
Valores zero: o que uma variável em Go guarda antes de receber qualquer valor. int: 0. float64: 0. string: "", a string vazia. bool: false. Ponteiros, slices, maps, canais, funções e interfaces: nil. O Go inicializa toda variável, então uma variável declarada é sempre segura de ler, ao contrário do C e do C++, em que uma variável local não inicializada guarda os bits que sobraram na memória

Números começam em 0, strings em "" (o verbo %q imprime a string com as aspas, para você ver que está vazia) e booleanos em false. É uma grande diferença em relação ao C e ao C++, em que ler uma variável não inicializada é um erro. Em Go é sempre seguro, e muitas vezes é exatamente o que você quer: um contador que começa em zero não precisa de = 0.

Deixe o Go descobrir o tipo

Quando você dá um valor inicial, o Go descobre o tipo sozinho. Dá para tirar o tipo do var, ou usar a declaração curta de variável :=:

1package main
2
3import "fmt"
4
5func main() {
6	var cidade = "Lisboa"
7	altura := 1.65
8	ano := 2024
9	estaAberto := true
10	fmt.Printf("%v é um %T\n", cidade, cidade)
11	fmt.Printf("%v é um %T\n", altura, altura)
12	fmt.Printf("%v é um %T\n", ano, ano)
13	fmt.Printf("%v é um %T\n", estaAberto, estaAberto)
14}
1Lisboa é um string
21.65 é um float64
32024 é um int
4true é um bool

O verbo %T imprime o tipo de um valor. Um número inteiro vira int, um número com ponto decimal vira float64, texto vira string, e true/false vira bool. A variável continua com tipo fixo; você só não precisou escrever o tipo.

O := é o que você mais vai ver em código Go, mas ele tem duas regras:

  • Só funciona dentro de funções. No nível do pacote (fora de qualquer função), use var.
  • Precisa declarar pelo menos uma variável nova. Para mudar uma variável que já existe, use o = simples.

Quebre as regras e o compilador avisa:

1package main
2
3import "fmt"
4
5func main() {
6	pontos := 10
7	pontos := 20
8	fmt.Println(pontos)
9}
$ go run .
# ola
.\main.go:7:9: no new variables on left side of :=

("Nenhuma variável nova do lado esquerdo do :=".)

1package main
2
3import "fmt"
4
5saudacao := "Olá"
6
7func main() {
8	fmt.Println(saudacao)
9}
$ go run .
# ola
.\main.go:5:1: syntax error: non-declaration statement outside function body

("Comando que não é declaração fora do corpo de uma função.") No nível do pacote, escreva var saudacao = "Olá".

Várias de uma vez, mudando e trocando

1package main
2
3import "fmt"
4
5var (
6	nomeDoApp = "Quiz de Go"
7	versao    = 2
8)
9
10func main() {
11	largura, altura := 1920, 1080
12	fmt.Println(nomeDoApp, versao, largura, altura)
13
14	pontos := 10
15	pontos = pontos + 5
16	pontos += 20
17	pontos++
18	fmt.Println("Pontos:", pontos)
19
20	esquerda, direita := "esquerda", "direita"
21	esquerda, direita = direita, esquerda
22	fmt.Println(esquerda, direita)
23}
Quiz de Go 2 1920 1080
Pontos: 36
direita esquerda
  • var ( … ) agrupa várias declarações, comum no nível do pacote.
  • largura, altura := 1920, 1080 declara duas variáveis numa linha.
  • = atribui um valor novo; +=, -=, *= e /= são atalhos, e o ++ soma um. (Em Go, pontos++ é um comando sozinho na linha, não uma expressão que dá para usar dentro de outra.)
  • esquerda, direita = direita, esquerda troca dois valores numa linha, sem variável temporária: o Go calcula o lado direito primeiro.

Os tipos básicos e os seus tamanhos

Os tipos básicos do Go e quantos bytes cada um ocupa, medidos com unsafe.Sizeof no Go 1.22, Windows de 64 bits. bool: 1 byte. byte, um apelido do uint8: 1 byte, para dados brutos. int8: 1 byte, até 127. int32 e rune: 4 bytes; um rune é um caractere Unicode. float32: 4 bytes. int: 8 bytes em sistemas de 64 bits, até cerca de 9,2 quintilhões; o dobro do int do C++ na mesma máquina. int64: 8 bytes. float64: 8 bytes, o padrão para decimais. string: 16 bytes, um ponteiro para o texto mais o tamanho dele; o texto em si fica em outro lugar
1package main
2
3import (
4	"fmt"
5	"math"
6	"unsafe"
7)
8
9func main() {
10	var pequeno int8
11	var inteiro int
12	var decimal float64
13	var letra rune
14	var texto string
15	fmt.Println("int8:", unsafe.Sizeof(pequeno), "byte, até", math.MaxInt8)
16	fmt.Println("int:", unsafe.Sizeof(inteiro), "bytes, até", math.MaxInt)
17	fmt.Println("float64:", unsafe.Sizeof(decimal), "bytes")
18	fmt.Println("rune:", unsafe.Sizeof(letra), "bytes")
19	fmt.Println("string:", unsafe.Sizeof(texto), "bytes")
20}
1int8: 1 byte, até 127
2int: 8 bytes, até 9223372036854775807
3float64: 8 bytes
4rune: 4 bytes
5string: 16 bytes

Os que você mais vai usar:

  • int para números inteiros. Em sistemas de 64 bits ele tem 8 bytes e guarda até cerca de 9,2 quintilhões. (Use int8, int16, int32 ou int64 quando precisar de um tamanho exato, e os tipos uint para números que nunca são negativos.)
  • float64 para números com casas decimais.
  • string para texto. Os 16 bytes são um ponteiro para o texto mais o tamanho dele; os caracteres em si ficam em outro lugar.
  • bool para true ou false.
  • rune para um único caractere Unicode, escrito entre aspas simples: 'A', 'ã'.

O Go nunca mistura tipos por acidente

Muitas linguagens convertem um int em decimal sem avisar quando você mistura os dois. O Go se recusa:

1package main
2
3import "fmt"
4
5func main() {
6	macas := 3
7	preco := 0.5
8	total := macas * preco
9	fmt.Println(total)
10}
$ go run .
# ola
.\main.go:8:11: invalid operation: macas * preco (mismatched types int and float64)

("Operação inválida: tipos que não combinam, int e float64.") Você converte explicitamente, escrevendo o tipo como se fosse uma chamada de função:

1package main
2
3import "fmt"
4
5func main() {
6	macas := 3
7	preco := 0.5
8	total := float64(macas) * preco
9	fmt.Println(total)
10
11	media := 7.9
12	fmt.Println(int(media))
13}
1.5
7

float64(macas) transforma 3 em 3.0. No sentido contrário, int(7.9) corta as casas decimais (dando 7, e não 8). Ser explícito quer dizer que você sempre vê onde acontece uma conversão. (O Go usa ponto, não vírgula, nos números.)

Números que não cabem

O Go pega alguns estouros já na compilação. Tente guardar 3 bilhões num int32:

1package main
2
3import "fmt"
4
5func main() {
6	var populacao int32 = 3000000000
7	fmt.Println(populacao)
8}
$ go run .
# ola
.\main.go:6:24: cannot use 3000000000 (untyped int constant) as int32 value in variable declaration (overflows)

Mas um estouro que acontece enquanto o programa roda dá a volta:

1package main
2
3import "fmt"
4
5func main() {
6	var nivel int8 = 127
7	nivel++
8	fmt.Println(nivel)
9}
-128
O que acontece quando um número não cabe em Go. Na compilação, uma constante que não cabe é um erro: var populacao int32 = 3000000000 dá cannot use 3000000000 (untyped int constant) as int32 value in variable declaration (overflows). Na execução, a conta dá a volta: um int8 guarda de -128 a 127, então 127 mais 1 vira -128, como um hodômetro que zera. Em Go esse comportamento é definido, ao contrário do estouro com sinal no C e no C++. Escolha um tipo grande o bastante para os seus dados; o int tem 8 bytes em sistemas de 64 bits

Um int8 guarda de -128 a 127, então 127 + 1 dá a volta e vira -128, como um hodômetro. Em Go esse comportamento é bem definido (no C e no C++, o estouro com sinal é comportamento indefinido), mas quase nunca é o que você quer: escolha um tipo grande o bastante para os seus dados, o que normalmente quer dizer só usar int.

Constantes e contas exatas

Valores que nunca mudam são constantes, declaradas com const:

1package main
2
3import "fmt"
4
5const Pi = 3.14159
6
7func main() {
8	const diasPorSemana = 7
9	raio := 2.0
10	fmt.Println("Área:", Pi*raio*raio)
11	fmt.Println("Horas por semana:", diasPorSemana*24)
12
13	fmt.Println(0.1 + 0.2)
14	primeiro := 0.1
15	segundo := 0.2
16	fmt.Println(primeiro + segundo)
17}
1Área: 12.56636
2Horas por semana: 168
30.3
40.30000000000000004

As duas últimas linhas mostram um detalhe lindo do Go. 0.1 + 0.2 escrito com constantes é calculado pelo compilador com aritmética exata, dando exatamente 0.3. A mesma soma com variáveis float64 acontece durante a execução, em ponto flutuante binário, em que 0.1 e 0.2 não podem ser guardados de forma exata, então o resultado sai um tiquinho errado. Lição: não compare decimais com ==, e conte dinheiro em centavos inteiros.

Mudar uma constante é erro de compilação (cannot assign to Pi (neither addressable nor a map index expression)), e uma constante não pode guardar um valor calculado durante a execução. (Constantes que começam com letra maiúscula, como Pi, são exportadas pelo pacote, assim como as funções.)

Strings: bytes vs. caracteres

1package main
2
3import (
4	"fmt"
5	"unicode/utf8"
6)
7
8func main() {
9	palavra := "Olá"
10	fmt.Println(len(palavra))
11	fmt.Println(utf8.RuneCountInString(palavra))
12	fmt.Println(palavra + ", mundo!")
13	var inicial rune = 'O'
14	fmt.Println(inicial, string(inicial))
15}
14
23
3Olá, mundo!
479 O
Strings em Go são bytes, caracteres são runes. A string "Olá" é guardada como 4 bytes em UTF-8: O ocupa 1 byte, 79; l ocupa 1 byte, 108; á ocupa 2 bytes, 195 e 161. Por isso len("Olá") dá 4, o número de bytes, enquanto utf8.RuneCountInString("Olá") dá 3, o número de caracteres. Um rune é um caractere Unicode; 'O' é o rune 79. Use len para bytes e RuneCountInString quando precisar do número de caracteres

As strings do Go são guardadas como bytes UTF-8. O len conta bytes, então "Olá" dá 4 (o á ocupa dois bytes); o utf8.RuneCountInString conta caracteres. Isso importa muito em português, cheio de acentos! Um rune é um caractere, e na verdade é um número (79 é o código do O). O + junta strings.

A regra da variável sem uso

Como você viu no primeiro programa, o Go se recusa a compilar código com uma variável que você nunca usa:

1package main
2
3import "fmt"
4
5func main() {
6	var idade int = 35
7	nome := "Ada"
8	fmt.Println(nome)
9}
$ go run .
# ola
.\main.go:6:6: idade declared and not used

Apague, use, ou (enquanto experimenta) atribua ao identificador vazio: _ = idade.

Dando nome às variáveis

  • Letras, dígitos e sublinhados, começando com letra ou sublinhado; os nomes diferenciam maiúsculas de minúsculas.
  • O estilo do Go é o camelCase: precoTotal, nomeDoUsuario. (Sublinhados são permitidos, mas não são o costume.)
  • Evite acentos nos nomes (preco, não preço): o Go até aceita, mas é fonte de confusão.
  • Letra maiúscula quer dizer visibilidade: um MaxUsuarios no nível do pacote é exportado para outros pacotes; maxUsuarios fica privado.
  • O Go prefere nomes curtos para escopos curtos: i para o índice de um laço ou err para um erro está bem por duas linhas, enquanto variáveis que vivem mais merecem nomes descritivos como segundosPassados.

Sua vez: pratique

Tente fazer estes antes de espiar as respostas. Coloque cada programa no seu próprio módulo e rode com go run ..

Exercício 1: Adivinhe a saída (aquecimento). O que isto imprime?

1package main
2
3import "fmt"
4
5func main() {
6	var total int
7	var rotulo string
8	contagem := 3
9	contagem *= 4
10	fmt.Printf("[%v] [%q] %d %T\n", total, rotulo, contagem, contagem/5)
11	fmt.Println(contagem/5, float64(contagem)/5)
12}

Exercício 2: Conserte o programa. Ele tem três erros. Ache-os rodando o programa.

1package main
2
3import "fmt"
4
5func main() {
6	preco := 9.99
7	quantidade := 3
8	quantidade := 4
9	var desconto float64
10	fmt.Println(preco * quantidade)
11}

Exercício 3: Troque e mostre os tipos. Declare primeiro := "chá" e segundo := 42.5, imprima os dois com os seus tipos usando %v (%T), depois declare xicara, caneca := "pequena", "grande", troque as duas numa linha e imprima.

Exercício 4: Minutos em horas. Leia uma quantidade de minutos com o fmt.Scan e imprima em horas e minutos. Por exemplo, 135 minutos são 2 h 15 min.

Exercício 5: Média das notas (desafio). Três notas são int: 7, 8 e 10. Imprima a média com duas casas decimais (8.33), e não o resultado inteiro (8).

Quiz rápido

  1. O que uma variável string declarada guarda antes de receber valor?
  2. Quando não dá para usar o :=?
  3. O que acontece com macas * preco quando macas é int e preco é float64?
  4. Por que len("Olá") dá 4?
  5. Qual é o tamanho de um int num computador de 64 bits?

Respostas

Exercício 1: total e rotulo guardam os valores zero; contagem vale 12; contagem/5 é uma divisão de int (2):

1package main
2
3import "fmt"
4
5func main() {
6	var total int
7	var rotulo string
8	contagem := 3
9	contagem *= 4
10	fmt.Printf("[%v] [%q] %d %T\n", total, rotulo, contagem, contagem/5)
11	fmt.Println(contagem/5, float64(contagem)/5)
12}
[0] [""] 12 int
2 2.4

Exercício 2: (1) quantidade := 4 redeclara uma variável: use quantidade = 4. (2) desconto é declarada e nunca usada: tire-a (ou use-a). (3) preco * quantidade mistura float64 e int: converta com float64(quantidade). Consertado:

1package main
2
3import "fmt"
4
5func main() {
6	preco := 9.99
7	quantidade := 3
8	quantidade = 4
9	fmt.Println(preco * float64(quantidade))
10}
39.96

Exercício 3: %v imprime o valor e %T o tipo; a troca usa a atribuição múltipla do Go:

1package main
2
3import "fmt"
4
5func main() {
6	primeiro := "chá"
7	segundo := 42.5
8	fmt.Printf("%v (%T)\n", primeiro, primeiro)
9	fmt.Printf("%v (%T)\n", segundo, segundo)
10
11	xicara, caneca := "pequena", "grande"
12	xicara, caneca = caneca, xicara
13	fmt.Println(xicara, caneca)
14}
chá (string)
42.5 (float64)
grande pequena

Exercício 4: A divisão inteira dá as horas, e o % os minutos que sobram:

1package main
2
3import "fmt"
4
5func main() {
6	var totalDeMinutos int
7	fmt.Print("Minutos: ")
8	fmt.Scan(&totalDeMinutos)
9	horas := totalDeMinutos / 60
10	minutos := totalDeMinutos % 60
11	fmt.Printf("%d h %d min\n", horas, minutos)
12}
Minutos: 135
2 h 15 min
Minutos: 59
0 h 59 min

Exercício 5: Somar os int dá 25; dividir por 3 como int daria 8, então converta a soma para float64 antes:

1package main
2
3import "fmt"
4
5func main() {
6	primeira, segunda, terceira := 7, 8, 10
7	soma := primeira + segunda + terceira
8	fmt.Println(soma / 3)
9	media := float64(soma) / 3
10	fmt.Printf("%.2f\n", media)
11}
8
8.33

Quiz: (1) A string vazia "", o seu valor zero. (2) Fora de funções (no nível do pacote), e quando nenhuma variável nova aparece do lado esquerdo. (3) Não compila: "mismatched types int and float64". Converta um dos lados explicitamente. (4) O len conta bytes, e o á ocupa dois bytes em UTF-8; o utf8.RuneCountInString dá 3 caracteres. (5) 8 bytes, guardando valores até cerca de 9,2 quintilhões.

Cola rápida

Eu quero…Escreva
Declarar com tipovar idade int (começa em 0)
Declarar e inicializarvar idade int = 35 ou var idade = 35
Declaração curta (em funções)idade := 35
Várias de uma vezlargura, altura := 1920, 1080
Agrupar no nível do pacotevar ( nome = "Go"; versao = 2 )
Mudar um valoridade = 36, idade += 1, idade++
Trocaresquerda, direita = direita, esquerda
Converter tiposfloat64(contagem), int(preco)
Uma constanteconst Pi = 3.14159
Imprimir valor e tipofmt.Printf("%v %T\n", x, x)
Bytes / caracteres de uma stringlen(texto) / utf8.RuneCountInString(texto)
Ignorar um valor_ = semUso

Resumindo

As variáveis do Go são simples, e rígidas de um jeito que ajuda:

  • var nome tipo declara; nome := valor declara e deduz o tipo (só dentro de funções).
  • Toda variável começa no seu valor zero: 0, "", false ou nil. Nada de lixo, nunca.
  • O int tem 8 bytes em sistemas de 64 bits, o float64 é o decimal padrão, e a string guarda bytes UTF-8.
  • Os tipos nunca se misturam por acidente: converta explicitamente com float64(x) ou int(y).
  • Estouro de constante é erro de compilação; estouro na execução dá a volta (127 + 1 num int8 vira -128).
  • Constantes são exatas (0.1 + 0.2 dá 0.3), enquanto variáveis float64 têm o arredondamento binário de sempre.
  • Variáveis sem uso não compilam, então o código Go fica arrumado.

Próximos passos na série: como instalar o Go no Linux e o laço for em Go. De volta ao começo: Seu Primeiro Programa em Go. Para ver o quadro geral, leia O Que É a Linguagem de Programação Go?

Obrigado pela leitura!

Conhece alguém que vai gostar? Mande para essa pessoa.

Mais artigos

Leia a seguir

Comentários

Seja gentil e específico. Comentários com links de contas novas aparecem depois da análise de um moderador.