Neste segundo artigo da série “Golang para Iniciantes”, vamos mergulhar nos fundamentos da linguagem. Você aprenderá tudo sobre variáveis, tipos de dados, funções, structs e métodos — os blocos de construção essenciais de qualquer programa Go.

Se você ainda não leu o primeiro artigo, recomendamos começar por lá:

📖 ← Artigo Anterior: Golang para Iniciantes: Primeiros Passos

Variáveis em Go

Go oferece várias formas de declarar variáveis, desde a forma explícita até a inferência de tipos.

Declaração Explícita com var

A forma tradicional de declarar variáveis:

package main

import "fmt"

func main() {
    // Declaração explícita: var nome tipo = valor
    var nome string = "João"
    var idade int = 30
    var salario float64 = 5000.50
    var ativo bool = true

    fmt.Printf("Nome: %s, Idade: %d, Salário: %.2f, Ativo: %t\n",
        nome, idade, salario, ativo)
}

Declaração com Inferência de Tipo

Go pode inferir o tipo automaticamente:

package main

import "fmt"

func main() {
    // Inferência de tipo: var nome = valor (tipo inferido)
    var nome = "João"      // string
    var idade = 30         // int
    var preco = 99.99      // float64
    var online = true      // bool

    fmt.Printf("Tipo de nome: %T\n", nome)    // string
    fmt.Printf("Tipo de idade: %T\n", idade)  // int
}

Declaração Curta com :=

A forma mais comum no dia a dia — apenas dentro de funções:

package main

import "fmt"

func main() {
    // Forma curta: nome := valor (só funciona dentro de funções)
    nome := "João"
    idade := 30
    pi := 3.14159

    fmt.Println(nome, idade, pi)
}

⚠️ Importante: O operador := só pode ser usado dentro de funções. Fora de funções (escopo de pacote), use var.

Declaração Múltipla

Declare várias variáveis de uma vez:

package main

import "fmt"

func main() {
    // Múltiplas variáveis do mesmo tipo
    var x, y, z int = 1, 2, 3

    // Múltiplas variáveis com tipos diferentes
    var (
        nome    string = "Maria"
        idade   int    = 25
        ativo   bool   = true
    )

    // Com inferência
    a, b, c := 1, 2.5, "texto"

    fmt.Println(x, y, z, nome, idade, ativo)
    fmt.Println(a, b, c)
}

Variáveis Não Inicializadas

Em Go, variáveis sempre têm um “zero value”:

package main

import "fmt"

func main() {
    var numero int       // 0
    var texto string     // "" (string vazia)
    var booleano bool    // false
    var ponteiro *int    // nil

    fmt.Printf("int: %d, string: %q, bool: %t, ponteiro: %v\n",
        numero, texto, booleano, ponteiro)
}

Zero values padrão:

TipoZero Value
int, float0, 0.0
string"" (vazia)
boolfalse
pointer, slice, map, channel, interfacenil
structCampos com seus zero values

Constantes com const

Valores que não podem ser alterados:

package main

import "fmt"

func main() {
    // Constantes
    const pi = 3.14159
    const versao = "1.0.0"
    const maxUsuarios = 100

    // Constantes agrupadas
    const (
        Segunda = 1
        Terca   = 2
        Quarta  = 3
        Quinta  = 4
        Sexta   = 5
    )

    fmt.Println("PI:", pi)
    fmt.Println("Dias úteis:", Segunda, Terca, Quarta, Quinta, Sexta)
}

Iota — gerador de constantes incrementais:

package main

import "fmt"

func main() {
    const (
        Domingo = iota  // 0
        Segunda         // 1
        Terca           // 2
        Quarta          // 3
        Quinta          // 4
        Sexta           // 5
        Sabado          // 6
    )

    fmt.Println("Segunda:", Segunda)  // 1
}

Tipos de Dados em Go

Go é uma linguagem estaticamente tipada com tipos básicos claros.

Tipos Numéricos

package main

import "fmt"

func main() {
    // Inteiros
    var inteiro int = 42           // Plataforma-dependente (32 ou 64 bits)
    var int8bit int8 = 127         // -128 a 127
    var int16bit int16 = 32767     // -32768 a 32767
    var int32bit int32 = 2147483647
    var int64bit int64 = 9223372036854775807

    // Inteiros sem sinal (unsigned)
    var uint8bit uint8 = 255       // 0 a 255
    var uint16bit uint16 = 65535
    var uint32bit uint32 = 4294967295
    var uint64bit uint64 = 18446744073709551615

    // Ponto flutuante
    var flutuante32 float32 = 3.14159
    var flutuante64 float64 = 3.141592653589793

    // Números complexos (raramente usados)
    var complexo complex64 = 1 + 2i
    var complexo128 complex128 = 1 + 2i

    fmt.Printf("int: %d (tipo: %T)\n", inteiro, inteiro)
    fmt.Printf("float64: %f (tipo: %T)\n", flutuante64, flutuante64)
}

Dica: Use int para inteiros gerais e float64 para ponto flutuante, a menos que tenha requisitos específicos de memória.

Strings

Strings em Go são sequências imutáveis de bytes:

package main

import "fmt"

func main() {
    // Declaração de strings
    saudacao := "Olá, Mundo!"
    citacao := `Esta é uma string
    com múltiplas linhas
    e "aspas" não precisam ser escapadas`

    // Concatenação
    nome := "João"
    sobrenome := "Silva"
    nomeCompleto := nome + " " + sobrenome

    // Tamanho
    tamanho := len(saudacao)

    // Acesso a caracteres (retorna byte)
    primeiro := saudacao[0]  // 'O' (byte)

    fmt.Println(nomeCompleto)
    fmt.Println("Tamanho:", tamanho)
    fmt.Printf("Primeiro caractere: %c\n", primeiro)

    // String como slice de bytes
    for i, c := range saudacao {
        fmt.Printf("Índice %d: %c\n", i, c)
    }
}

Booleanos

package main

import "fmt"

func main() {
    var ativo bool = true
    inativo := false

    // Operações lógicas
    resultado1 := ativo && inativo  // AND: false
    resultado2 := ativo || inativo  // OR: true
    resultado3 := !ativo            // NOT: false

    fmt.Println(resultado1, resultado2, resultado3)
}

Operadores

Operadores Aritméticos

package main

import "fmt"

func main() {
    a, b := 10, 3

    fmt.Println("Soma:", a+b)        // 13
    fmt.Println("Subtração:", a-b)   // 7
    fmt.Println("Multiplicação:", a*b) // 30
    fmt.Println("Divisão:", a/b)     // 3 (divisão inteira)
    fmt.Println("Módulo:", a%b)      // 1 (resto)

    // Incremento e decremento (somente como statements)
    a++  // a = a + 1
    b--  // b = b - 1
}

Nota: Go não tem operadores de pré/pós-incremento como expressões (++a ou --b não retornam valores).

Operadores de Comparação

package main

import "fmt"

func main() {
    x, y := 5, 10

    fmt.Println("Igual:", x == y)      // false
    fmt.Println("Diferente:", x != y)  // true
    fmt.Println("Maior:", x > y)       // false
    fmt.Println("Menor:", x < y)       // true
    fmt.Println("Maior ou igual:", x >= y) // false
    fmt.Println("Menor ou igual:", x <= y) // true
}

Funções

Funções são cidadãs de primeira classe em Go.

Declaração Básica

package main

import "fmt"

// Função simples
func saudar(nome string) {
    fmt.Printf("Olá, %s!\n", nome)
}

// Função com retorno
func somar(a, b int) int {
    return a + b
}

// Função com múltiplos parâmetros do mesmo tipo
func calcularArea(largura, altura float64) float64 {
    return largura * altura
}

func main() {
    saudar("Maria")
    resultado := somar(10, 20)
    area := calcularArea(5.0, 3.0)

    fmt.Println("Soma:", resultado)
    fmt.Println("Área:", area)
}

Múltiplos Retornos

Uma característica poderosa de Go:

package main

import "fmt"

// Retorna quociente e resto
func dividir(dividendo, divisor int) (int, int) {
    quociente := dividendo / divisor
    resto := dividendo % divisor
    return quociente, resto
}

// Retorna resultado e erro (padrão idiomático)
func dividirSeguro(dividendo, divisor float64) (float64, error) {
    if divisor == 0 {
        return 0, fmt.Errorf("divisão por zero")
    }
    return dividendo / divisor, nil
}

func main() {
    // Ignorando um retorno com _
    q, _ := dividir(17, 5)
    fmt.Println("Quociente:", q)

    q2, r := dividir(17, 5)
    fmt.Printf("%d dividido por %d = %d, resto %d\n", 17, 5, q2, r)

    // Tratando erro
    resultado, err := dividirSeguro(10.0, 0.0)
    if err != nil {
        fmt.Println("Erro:", err)
    } else {
        fmt.Println("Resultado:", resultado)
    }
}

Named Return Values

Retornos nomeados tornam o código mais claro:

package main

import "fmt"

// Retornos nomeados
func calcularStats(numeros []int) (min, max, media int) {
    if len(numeros) == 0 {
        return 0, 0, 0  // Retorna zero values
    }

    min = numeros[0]
    max = numeros[0]
    soma := 0

    for _, n := range numeros {
        if n < min {
            min = n
        }
        if n > max {
            max = n
        }
        soma += n
    }

    media = soma / len(numeros)
    return  // Retorna min, max, media automaticamente
}

func main() {
    nums := []int{5, 2, 8, 1, 9}
    minimo, maximo, media := calcularStats(nums)
    fmt.Printf("Min: %d, Max: %d, Média: %d\n", minimo, maximo, media)
}

Funções Variádicas

Aceitam número variável de argumentos:

package main

import "fmt"

// Função variádica
func somarTudo(numeros ...int) int {
    total := 0
    for _, n := range numeros {
        total += n
    }
    return total
}

func main() {
    fmt.Println(somarTudo(1, 2, 3))       // 6
    fmt.Println(somarTudo(10, 20))        // 30
    fmt.Println(somarTudo())              // 0

    // Passando slice com ...
    nums := []int{1, 2, 3, 4, 5}
    fmt.Println(somarTudo(nums...))       // 15
}

Funções Anônimas e Closures

package main

import "fmt"

func main() {
    // Função anônima atribuída a variável
    dobro := func(x int) int {
        return x * 2
    }

    fmt.Println(dobro(5))  // 10

    // Closure (função que captura variáveis do escopo)
    contador := func() func() int {
        count := 0
        return func() int {
            count++
            return count
        }
    }()

    fmt.Println(contador())  // 1
    fmt.Println(contador())  // 2
    fmt.Println(contador())  // 3
}

Structs (Estruturas)

Structs agrupam campos relacionados:

Declaração e Uso

package main

import "fmt"

type Pessoa struct {
    Nome    string
    Idade   int
    Email   string
    Ativo   bool
}

func main() {
    // Criação de instância
    p1 := Pessoa{
        Nome:  "João Silva",
        Idade: 30,
        Email: "joao@exemplo.com",
        Ativo: true,
    }

    // Forma curta (ordem dos campos)
    p2 := Pessoa{"Maria Santos", 25, "maria@exemplo.com", true}

    // Struct parcial (campos omitidos ficam com zero value)
    p3 := Pessoa{
        Nome: "Pedro",
    }

    // Acesso aos campos
    fmt.Println("Nome:", p1.Nome)
    fmt.Println("Idade:", p1.Idade)

    // Modificação
    p1.Idade = 31
    fmt.Println("Nova idade:", p1.Idade)

    fmt.Printf("Pessoa 2: %+v\n", p2)
    fmt.Printf("Pessoa 3: %+v\n", p3)
}

Structs Aninhados

package main

import "fmt"

type Endereco struct {
    Rua     string
    Cidade  string
    Estado  string
    CEP     string
}

type Pessoa struct {
    Nome     string
    Idade    int
    Endereco Endereco  // Struct aninhado
}

func main() {
    pessoa := Pessoa{
        Nome:  "Ana Paula",
        Idade: 28,
        Endereco: Endereco{
            Rua:    "Av. Brasil, 123",
            Cidade: "São Paulo",
            Estado: "SP",
            CEP:    "01000-000",
        },
    }

    fmt.Printf("%s mora em %s, %s\n",
        pessoa.Nome,
        pessoa.Endereco.Cidade,
        pessoa.Endereco.Estado)
}

Ponteiros para Structs

package main

import "fmt"

type Pessoa struct {
    Nome  string
    Idade int
}

// Recebe ponteiro para modificar o original
func fazerAniversario(p *Pessoa) {
    p.Idade++  // Não precisa de (*p).Idade, Go faz automaticamente
}

func main() {
    p := Pessoa{"João", 30}

    fmt.Println("Antes:", p.Idade)  // 30

    fazerAniversario(&p)

    fmt.Println("Depois:", p.Idade)  // 31
}

Métodos

Métodos são funções associadas a tipos (especialmente structs):

package main

import "fmt"

type Retangulo struct {
    Largura  float64
    Altura   float64
}

// Método com receiver por valor (não modifica o original)
func (r Retangulo) Area() float64 {
    return r.Largura * r.Altura
}

// Método com receiver por ponteiro (pode modificar o original)
func (r *Retangulo) Escalar(fator float64) {
    r.Largura *= fator
    r.Altura *= fator
}

// Método com retorno formatado
func (r Retangulo) String() string {
    return fmt.Sprintf("Retângulo %.1f x %.1f", r.Largura, r.Altura)
}

func main() {
    ret := Retangulo{Largura: 10, Altura: 5}

    fmt.Println(ret.String())        // Retângulo 10.0 x 5.0
    fmt.Println("Área:", ret.Area()) // 50

    ret.Escalar(2)
    fmt.Println("Após escalar:", ret.String()) // Retângulo 20.0 x 10.0
}

Quando Usar Ponteiro vs Valor?

Use valor (r Retangulo) quando…Use ponteiro (r *Retangulo) quando…
Não precisa modificar o receiverPrecisa modificar o receiver
Struct é pequena (copia é barata)Struct é grande (evita cópia)
Thread safety é importantePerformance crítica

Type Conversion (Conversão de Tipos)

Go não faz conversão implícita — você deve converter explicitamente:

package main

import "fmt"
import "strconv"

func main() {
    // Entre tipos numéricos
    var i int = 42
    var f float64 = float64(i)  // Conversão explícita obrigatória
    var u uint = uint(i)

    // String para número
    numStr := "123"
    num, _ := strconv.Atoi(numStr)  // string → int
    num64, _ := strconv.ParseInt(numStr, 10, 64)

    // Número para string
    str := strconv.Itoa(num)        // int → string
    strf := strconv.FormatFloat(3.14, 'f', 2, 64)

    // String para bytes e vice-versa
    texto := "Olá"
    bytes := []byte(texto)
    texto2 := string(bytes)

    fmt.Printf("int: %d, float64: %f, uint: %d\n", i, f, u)
    fmt.Printf("Número: %d, String: %s\n", num, str)
    fmt.Println(texto2)
}

Exercícios Práticos

Exercício 1: Calculadora de IMC

Crie uma função que calcula o IMC (Índice de Massa Corporal) dados peso e altura.

Ver solução
package main

import "fmt"

func calcularIMC(peso, altura float64) float64 {
    return peso / (altura * altura)
}

func classificarIMC(imc float64) string {
    switch {
    case imc < 18.5:
        return "Abaixo do peso"
    case imc < 25:
        return "Peso normal"
    case imc < 30:
        return "Sobrepeso"
    default:
        return "Obesidade"
    }
}

func main() {
    peso := 70.0
    altura := 1.75

    imc := calcularIMC(peso, altura)
    classificacao := classificarIMC(imc)

    fmt.Printf("IMC: %.2f - %s\n", imc, classificacao)
}

Exercício 2: Sistema de Biblioteca

Crie um sistema simples de biblioteca com struct Livro e métodos para emprestar e devolver.

Ver solução
package main

import "fmt"

type Livro struct {
    Titulo    string
    Autor     string
    Emprestado bool
}

func (l *Livro) Emprestar() error {
    if l.Emprestado {
        return fmt.Errorf("livro já está emprestado")
    }
    l.Emprestado = true
    return nil
}

func (l *Livro) Devolver() error {
    if !l.Emprestado {
        return fmt.Errorf("livro não estava emprestado")
    }
    l.Emprestado = false
    return nil
}

func (l Livro) String() string {
    status := "Disponível"
    if l.Emprestado {
        status = "Emprestado"
    }
    return fmt.Sprintf("%s (por %s) - %s", l.Titulo, l.Autor, status)
}

func main() {
    livro := Livro{Titulo: "O Senhor dos Anéis", Autor: "J.R.R. Tolkien"}

    fmt.Println(livro)

    if err := livro.Emprestar(); err != nil {
        fmt.Println("Erro:", err)
    } else {
        fmt.Println("Livro emprestado com sucesso!")
    }

    fmt.Println(livro)
}

Exercício 3: Gerenciador de Tarefas

Implemente um gerenciador de tarefas com structs e funções para adicionar, listar e completar tarefas.

Ver solução
package main

import "fmt"
import "time"

type Tarefa struct {
    ID        int
    Titulo    string
    Completa  bool
    CriadaEm  time.Time
}

type GerenciadorTarefas struct {
    tarefas []Tarefa
    proxID  int
}

func (gt *GerenciadorTarefas) Adicionar(titulo string) Tarefa {
    tarefa := Tarefa{
        ID:       gt.proxID,
        Titulo:   titulo,
        Completa: false,
        CriadaEm: time.Now(),
    }
    gt.tarefas = append(gt.tarefas, tarefa)
    gt.proxID++
    return tarefa
}

func (gt *GerenciadorTarefas) Completar(id int) bool {
    for i := range gt.tarefas {
        if gt.tarefas[i].ID == id {
            gt.tarefas[i].Completa = true
            return true
        }
    }
    return false
}

func (gt GerenciadorTarefas) ListarPendentes() []Tarefa {
    var pendentes []Tarefa
    for _, t := range gt.tarefas {
        if !t.Completa {
            pendentes = append(pendentes, t)
        }
    }
    return pendentes
}

func main() {
    gt := GerenciadorTarefas{}

    gt.Adicionar("Estudar Go")
    gt.Adicionar("Fazer exercícios")
    gt.Adicionar("Ler documentação")

    gt.Completar(0)

    fmt.Println("Tarefas pendentes:")
    for _, t := range gt.ListarPendentes() {
        fmt.Printf("- %s\n", t.Titulo)
    }
}

Dicas e Melhores Práticas

1. Nomenclatura

  • Use camelCase para variáveis e funções privadas
  • Use PascalCase para tipos, funções e variáveis exportadas
  • Use SCREAMING_SNAKE_CASE para constantes
const MAX_SIZE = 100  // Constante exportada
var contador = 0      // Variável privada
func calcular() {}    // Função privada
func Calcular() {}    // Função exportada (pública)

2. Zero Values são Seus Amigos

Aproveite que Go inicializa variáveis automaticamente:

// Bom: aproveita zero values
var contador int  // Começa em 0
var encontrado bool // Começa em false

// Ruim: inicialização desnecessária
contador := 0
encontrado := false

3. Evite Variáveis Globais

Prefira passar dependências como parâmetros:

// Ruim: variável global
var db *sql.DB

func main() {
    db = conectarDB()
}

// Bom: injeção de dependência
func main() {
    db := conectarDB()
    handler := NewHandler(db)
}

4. Trate Erros Explicitamente

Go não tem exceções — verifique erros:

// Ruim: ignorando erro
file, _ := os.Open("dados.txt")

// Bom: tratando erro
file, err := os.Open("dados.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

Próximos Passos

Parabéns! Você domina agora os fundamentos da sintaxe de Go. Continue sua jornada:

Próximo Artigo da Série

📖 Controle de Fluxo em Go: if, switch, loops → — Aprenda a controlar o fluxo de execução com condicionais e loops.

Recursos Recomendados

Carreira Go

Desafios

  1. Crie uma calculadora completa com operações básicas
  2. Implemente um jogo da forca em linha de comando
  3. Crie um sistema de cadastro de produtos

Artigo anterior: Golang para Iniciantes: Primeiros Passos ←
Próximo artigo: Controle de Fluxo em Go: if, switch, loops →


Última atualização: 09 de fevereiro de 2026
Versão do Go: 1.23