Variáveis e tipos básicos
por Beckett HollisEntenda o conceito
Em Go, toda variável tem um tipo definido na compilação e ele nunca muda. Há duas formas principais de declarar: var nome tipo = valor, que funciona em qualquer lugar, e a declaração curta nome := valor, que só existe dentro de funções e deduz o tipo pelo valor à direita. Depois de declarada, a variável recebe novos valores com =, sempre do mesmo tipo.
Os tipos básicos do dia a dia são int (inteiros), float64 (decimais), string (texto em UTF-8) e bool (true ou false). Também existem tipos com tamanho fixo, como int64 e uint8 (que é o mesmo que byte), e rune, que representa um caractere Unicode. Uma variável declarada sem valor não fica "vazia": ela recebe o valor zero do tipo, que é 0, 0.0, "" ou false.
Go não converte tipos sozinho. Somar um int com um float64 não compila; você converte com a sintaxe T(v), como float64(quantidade). Cuidado com dois detalhes: a divisão entre inteiros descarta a parte decimal (10 / 4 dá 2), e int(7.9) corta as casas, dando 7, sem arredondar.
var x int = 5declara em qualquer lugar;x := 5é a forma curta, só dentro de funções, e deduz o tipo.- Variável sem valor inicial recebe o valor zero do tipo:
0para números,""parastringefalseparabool. - Depois de declarada, a variável muda com
=; usar:=de novo com o mesmo nome, no mesmo escopo, não compila. - Tipos diferentes não se misturam: converta com
T(v), comofloat64(qtd) * preco;int(x)corta as casas decimais. - O verbo
%TdoPrintfmostra o tipo de um valor;lende umastringconta bytes, não letras.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Inventário do herói com var e valores zero
package main
import "fmt"
func main() {
// var com tipo e sem valor: a variável nasce com o valor zero do tipo.
var heroi string
var nivel int
var ouro float64
var temEspada bool
fmt.Println("Antes de começar o jogo:")
fmt.Printf("heroi=%q nivel=%d ouro=%.1f temEspada=%t\n", heroi, nivel, ouro, temEspada)
// Depois, cada variável recebe um valor do mesmo tipo.
heroi = "Lia"
nivel = 1
ouro = 15.5
temEspada = true
fmt.Println("Depois da primeira fase:")
fmt.Printf("heroi=%q nivel=%d ouro=%.1f temEspada=%t\n", heroi, nivel, ouro, temEspada)
// var com valor inicial: o tipo pode ser escrito ou deduzido.
var vidas int = 3
var poder = 7.25
fmt.Println("Vidas:", vidas, "| Poder:", poder)
}
As quatro variáveis são declaradas com var e o tipo, mas sem valor. Mesmo assim o programa compila e imprime: cada uma recebeu o valor zero do seu tipo ("", 0, 0.0 e false). Em Go não existe variável "não inicializada" com lixo de memória.
No fim aparecem as duas variações de var com valor: com o tipo escrito (var vidas int = 3) e com o tipo deduzido (var poder = 7.25, que vira float64). O verbo %t imprime booleanos e o %q deixa visível a string vazia.
02Empréstimo na biblioteca com := e %T
package main
import "fmt"
func main() {
// := declara e atribui; o tipo vem do valor à direita.
titulo := "Dom Casmurro"
exemplares := 4
multaDiaria := 0.75
disponivel := exemplares > 0
// %T mostra o tipo que o compilador deduziu.
fmt.Printf("%-12s %-14v %T\n", "titulo", titulo, titulo)
fmt.Printf("%-12s %-14v %T\n", "exemplares", exemplares, exemplares)
fmt.Printf("%-12s %-14v %T\n", "multaDiaria", multaDiaria, multaDiaria)
fmt.Printf("%-12s %-14v %T\n", "disponivel", disponivel, disponivel)
// Várias variáveis de uma vez, na mesma linha.
leitor, diasAtraso := "Paulo", 6
// Reatribuição usa = (a variável já existe).
exemplares = exemplares - 1
fmt.Println()
fmt.Printf("%s pegou %q; restam %d exemplares.\n", leitor, titulo, exemplares)
fmt.Printf("Atraso de %d dias: multa de R$ %.2f\n", diasAtraso, float64(diasAtraso)*multaDiaria)
}
A declaração curta := é a forma mais comum dentro de funções: o tipo vem do valor. A tabela usa %T para mostrar o que o compilador deduziu: texto vira string, número sem ponto vira int, número com ponto vira float64 e uma comparação vira bool.
Repare que exemplares = exemplares - 1 usa =, porque a variável já existe. E a multa só pode ser calculada convertendo diasAtraso para float64, já que Go não multiplica int por float64 diretamente.
03Estoque da loja: divisão inteira, conversões e bytes
package main
import "fmt"
func main() {
// Tipos com tamanho definido: uint8 vai de 0 a 255.
var prateleira uint8 = 12
var codigo int64 = 789123456789
estoque := 47 // int
porCaixa := 6 // int
precoCaixa := 54.90 // float64
// Divisão entre inteiros descarta a parte decimal; % dá o resto.
caixas := estoque / porCaixa
avulsas := estoque % porCaixa
fmt.Printf("Prateleira %d, código %d\n", prateleira, codigo)
fmt.Printf("%d unidades = %d caixas + %d avulsas\n", estoque, caixas, avulsas)
// Para misturar int e float64, converta com T(v).
valorCaixas := float64(caixas) * precoCaixa
fmt.Printf("Valor das caixas: R$ %.2f\n", valorCaixas)
// int(x) corta as casas decimais, sem arredondar.
precoUnitario := precoCaixa / float64(porCaixa)
fmt.Printf("Preço unitário: %.4f -> int: %d\n", precoUnitario, int(precoUnitario))
// len conta bytes; letras acentuadas ocupam mais de um byte.
nome := "Açúcar"
fmt.Println(nome, "tem", len(nome), "bytes e", len([]rune(nome)), "letras")
}
Este exemplo junta os pontos que mais causam erro. estoque / porCaixa com dois inteiros dá 7, sem casas, e % devolve o resto (5). Para multiplicar as caixas pelo preço é preciso float64(caixas), e int(precoUnitario) mostra que a conversão para inteiro corta o 9.15 em 9, sem arredondar.
A última linha mostra que string em Go guarda bytes em UTF-8: Açúcar tem 6 letras, mas ç e ú ocupam 2 bytes cada, então len devolve 8. Para contar letras, converta para []rune. Também aparecem os tipos com tamanho fixo uint8 e int64.
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Ficha da personagem com %TFácil
Monte a ficha de uma personagem de jogo com cinco atributos (nome, classe, nível, vida e se está em missão) e imprima cada valor seguido do seu tipo, como na saída esperada.
- Usa as três formas de declarar:
varcom tipo,varsem tipo e:=. - Há pelo menos uma variável de cada tipo:
string,int,float64ebool. - Cada linha é impressa com
fmt.Printfusando%vpara o valor e%Tpara o tipo. - A saída é idêntica à esperada.
package main
import "fmt"
func main() {
var nome string = "Tora"
// declare classe, nivel, vida e emMissao
fmt.Printf("nome: %v (%T)\n", nome, nome)
// imprima os outros atributos
}
nome: Tora (string)
classe: arqueira (string)
nivel: 7 (int)
vida: 82.5 (float64)
emMissao: true (bool)
Ver dica
O tipo deduzido depende de como o valor é escrito: 82.5 e 82 não viram o mesmo tipo.
Ver resposta
package main
import "fmt"
func main() {
// Cada atributo do personagem com o tipo que combina com ele.
var nome string = "Tora"
var classe = "arqueira"
nivel := 7
vida := 82.5
emMissao := true
// %v mostra o valor e %T mostra o tipo.
fmt.Printf("nome: %v (%T)\n", nome, nome)
fmt.Printf("classe: %v (%T)\n", classe, classe)
fmt.Printf("nivel: %v (%T)\n", nivel, nivel)
fmt.Printf("vida: %v (%T)\n", vida, vida)
fmt.Printf("emMissao: %v (%T)\n", emMissao, emMissao)
}
Passo a passo
var nome string = "Tora"é a forma completa: palavravar, nome, tipo e valor.var classe = "arqueira"omite o tipo; o Go deduzstringpelo valor entre aspas.nivel := 7usa a declaração curta; um número sem ponto viraint.vida := 82.5tem ponto decimal, então virafloat64, o tipo decimal padrão do Go.emMissao := truevirabool, o tipo que só aceitatrueoufalse.- Cada
fmt.Printfpassa a mesma variável duas vezes: uma para o%v(valor) e outra para o%T(tipo).
Por que funciona
As três formas de declaração produzem variáveis iguais em tudo: o que muda é só quanto você escreve. Quando o tipo não aparece, o compilador o deduz pelo valor literal, e o %T revela essa decisão na saída. Por isso 82.5 aparece como float64 e 7 como int.
O %v imprime qualquer valor no formato padrão, então serve para os cinco tipos sem trocar de verbo.
Erros comuns
- Escrever
vida := 82achando que vira decimal: o tipo deduzido éinte a saída mostra82 (int). Escreva82.5(ou82.0). - Usar
nivel := 7fora de uma função, no nível do pacote: dá erro de compilação (non-declaration statement outside function body). Lá fora só valevar. - Declarar uma variável e não usá-la, como
var apelido = "Tô"sem imprimir: o compilador recusa comdeclared and not used. - Passar a variável só uma vez, como
fmt.Printf("nivel: %v (%T)\n", nivel): o segundo verbo fica sem valor e aparece%!T(MISSING).
Outra forma de resolver
Um bloco var ( ... ) agrupa as declarações e deixa a ficha mais organizada:
var (
nome = "Tora"
classe = "arqueira"
nivel = 7
vida = 82.5
emMissao = true
)
Ele é útil para várias variáveis relacionadas, inclusive no nível do pacote, onde := não é permitido.
Saída esperada
nome: Tora (string)
classe: arqueira (string)
nivel: 7 (int)
vida: 82.5 (float64)
emMissao: true (bool)
02Total do carrinho com cupomMédio
Calcule o total de um carrinho com 3 unidades de um produto de R$ 12,90 e um cupom de 10% de desconto. A quantidade e o percentual do cupom são inteiros; o preço é decimal.
quantidadeecupomsãoint;precoUnitarioéfloat64.- Os inteiros são convertidos com
float64(...)antes de entrar nas contas com o preço. - Subtotal, desconto e total são calculados no programa e impressos com
%.2f. - O símbolo de porcentagem aparece na saída usando
%%.
package main
import "fmt"
func main() {
quantidade := 3
precoUnitario := 12.90
cupom := 10
// calcule subtotal, desconto e total
}
Itens: 3 x R$ 12.90
Subtotal: R$ 38.70
Cupom de 10%: -R$ 3.87
Total: R$ 34.83
Ver dica
Se o compilador reclamar de mismatched types int and float64, é sinal de que falta uma conversão.
Ver resposta
package main
import "fmt"
func main() {
// Dados do carrinho: quantidade e cupom são inteiros, preço é decimal.
quantidade := 3
precoUnitario := 12.90
cupom := 10 // percentual de desconto
// float64(...) converte os inteiros antes de misturar com o preço.
subtotal := float64(quantidade) * precoUnitario
desconto := subtotal * float64(cupom) / 100
total := subtotal - desconto
fmt.Printf("Itens: %d x R$ %.2f\n", quantidade, precoUnitario)
fmt.Printf("Subtotal: R$ %.2f\n", subtotal)
fmt.Printf("Cupom de %d%%: -R$ %.2f\n", cupom, desconto)
fmt.Printf("Total: R$ %.2f\n", total)
}
Passo a passo
quantidade := 3ecupom := 10viramint, porque são números sem ponto;precoUnitario := 12.90virafloat64.float64(quantidade) * precoUnitarioconverte a quantidade antes de multiplicar, já que Go não misturaintcomfloat64.subtotal * float64(cupom) / 100calcula o desconto; o cupom também precisa de conversão.total := subtotal - descontojá opera só comfloat64, então não precisa converter nada.%.2fmostra os valores em reais com duas casas, e%dimprime os inteiros.- Em
Cupom de %d%%, o%%imprime o próprio símbolo de porcentagem.
Por que funciona
Go exige que os dois lados de uma operação tenham o mesmo tipo. A conversão float64(v) cria um valor decimal a partir do inteiro, e a partir daí toda a conta acontece em float64, sem perder as casas decimais.
Manter quantidade e cupom como int é a escolha natural: não existe meia unidade nem cupom de 10,5%. A conversão fica só no ponto em que eles encontram o preço.
Erros comuns
- Escrever
subtotal := quantidade * precoUnitario: não compila, com o erroinvalid operation: mismatched types int and float64. Converta comfloat64(quantidade). - Calcular
float64(cupom / 100): a divisão inteira acontece antes,10 / 100dá0, e o desconto sai zero. Converta antes de dividir. - Usar
%dpara o subtotal: o programa compila, mas imprime%!d(float64=38.7). Use%.2fparafloat64. - Escrever
%sozinho emCupom de %d%: oPrintftenta ler um verbo e a saída fica com%!no meio. Dobre para%%.
Outra forma de resolver
Dá para converter o cupom em fator de multiplicação logo no início:
fator := 1 - float64(cupom)/100
total := float64(quantidade) * precoUnitario * fator
Fica mais curto quando só o total importa; a versão com variáveis separadas é melhor quando o cupom precisa mostrar subtotal e desconto.
Saída esperada
Itens: 3 x R$ 12.90
Subtotal: R$ 38.70
Cupom de 10%: -R$ 3.87
Total: R$ 34.83
03Receita escalonada sem meio ovoDesafio
Uma receita rende 4 porções com 3 ovos e 250 g de farinha. Ajuste as quantidades para 10 porções, mostrando o fator de escala, a farinha, a conta exata de ovos, quantos ovos comprar (arredondando para cima) e por que int() e a divisão inteira dariam o resultado errado.
- O fator de escala é calculado em
float64, convertendo as porções antes da divisão. - Os ovos a comprar são um
intobtido commath.Ceile conversão explícita. - O programa mostra também o resultado de
int()direto e da divisão inteira10/4, para comparação. - A última linha usa
%Tpara mostrar os tipos do fator e dos ovos.
package main
import (
"fmt"
"math"
)
func main() {
const porcoesBase = 4
ovosBase := 3
farinhaBase := 250.0
porcoesNovas := 10
// calcule o fator e as novas quantidades
_ = math.Ceil
}
Fator de escala: 2.50
Farinha: 625 g
Ovos (conta exata): 7.5
Ovos (comprar): 8
Se usasse int(): 7
Divisão inteira 10/4: 2
Tipos: fator=float64, ovos=int
Ver dica
math.Ceil recebe e devolve float64; para ter um inteiro no fim, a conversão vem por fora da chamada.
Ver resposta
package main
import (
"fmt"
"math"
)
func main() {
// Receita original: rende 4 porções.
const porcoesBase = 4
ovosBase := 3
farinhaBase := 250.0 // gramas
porcoesNovas := 10
// Fator de escala em float64; com inteiros, 10 / 4 daria 2.
fator := float64(porcoesNovas) / float64(porcoesBase)
farinha := farinhaBase * fator
ovosExatos := float64(ovosBase) * fator
// Ovo não se divide: arredonda para cima e volta a ser int.
ovos := int(math.Ceil(ovosExatos))
fmt.Printf("Fator de escala: %.2f\n", fator)
fmt.Printf("Farinha: %.0f g\n", farinha)
fmt.Printf("Ovos (conta exata): %.1f\n", ovosExatos)
fmt.Printf("Ovos (comprar): %d\n", ovos)
fmt.Printf("Se usasse int(): %d\n", int(ovosExatos))
fmt.Printf("Divisão inteira 10/4: %d\n", porcoesNovas/porcoesBase)
fmt.Printf("Tipos: fator=%T, ovos=%T\n", fator, ovos)
}
Passo a passo
const porcoesBase = 4é uma constante sem tipo; ela se adapta ao contexto, mas aqui é convertida explicitamente para deixar a intenção clara.farinhaBase := 250.0tem ponto decimal para nascerfloat64;ovosBaseeporcoesNovassãoint, porque ovos e porções são contados inteiros.float64(porcoesNovas) / float64(porcoesBase)converte antes de dividir e dá2.5; sem a conversão, seria2.farinhaBase * fatorjá está todo emfloat64;%.0fmostra os gramas sem casas decimais.float64(ovosBase) * fatordá7.5, a conta exata, que ainda éfloat64.int(math.Ceil(ovosExatos))arredonda para cima (8.0) e só depois converte paraint, porque ninguém compra meio ovo.- As linhas de comparação mostram que
int(7.5)corta para7e que10/4entre inteiros dá2. %Tconfirma no fim que o fator éfloat64e os ovos a comprar sãoint.
Por que funciona
O programa separa duas grandezas: medidas contínuas, como o fator e a farinha, ficam em float64; quantidades contáveis, como ovos, terminam em int. A ponte entre elas é sempre uma conversão explícita, e o lugar dela muda o resultado: converter antes da divisão preserva o .5, converter depois descarta.
Como int() corta as casas e não arredonda, quem decide a regra de arredondamento é o pacote math. math.Ceil sobe para o próximo inteiro, que é o que a receita precisa para não faltar ingrediente.
Erros comuns
- Calcular
fator := float64(porcoesNovas / porcoesBase): a divisão inteira acontece primeiro e o fator vira2.00, deixando a receita com 500 g de farinha. Converta cada operando antes de dividir. - Usar
ovos := int(ovosExatos)achando que arredonda: o resultado é7e falta um ovo. A conversão só corta; usemath.Ceil(oumath.Round, se a regra for o mais próximo). - Escrever
ovos := math.Ceil(ovosExatos)e imprimir com%d: o valor éfloat64e sai%!d(float64=8). Converta comint(...). - Declarar
farinhaBase := 250sem o ponto: ela viraintefarinhaBase * fatornão compila (mismatched types int and float64).
Outra forma de resolver
Com aritmética inteira também dá para arredondar para cima, sem float64 e sem o pacote math:
ovos := (ovosBase*porcoesNovas + porcoesBase - 1) / porcoesBase
A conta (30 + 3) / 4 dá 8. Ela é preferível quando todos os valores são inteiros e você quer evitar qualquer imprecisão de ponto flutuante; a versão com math.Ceil é mais fácil de ler.
Saída esperada
Fator de escala: 2.50
Farinha: 625 g
Ovos (conta exata): 7.5
Ovos (comprar): 8
Se usasse int(): 7
Divisão inteira 10/4: 2
Tipos: fator=float64, ovos=int