Go Admin

O que Todo Dev Deve Saber sobre Variáveis, Constantes, Tipos Primitivos e Inferência de Tipos em Go Já leu

Variáveis em Go: Declaração, Atribuição e Escopo Uma variável é um local na memória que armazena um valor. Em Go, você declara uma variável antes de usá-la, e a linguagem oferece múltiplas formas de declaração, cada uma com seu propósito específico. A sintaxe mais comum é usar a palavra-chave , seguida do nome da variável, seu tipo e o valor inicial. Go é uma linguagem fortemente tipada, o que significa que cada variável possui um tipo específico. Isso oferece segurança em tempo de compilação e facilita a detecção de erros. Veja como declarar variáveis das formas mais comuns: O operador é muito importante em Go e só funciona dentro de funções. Ele declara e atribui um valor simultaneamente, deixando a linguagem inferir o tipo automaticamente. Variáveis não utilizadas causarão erro de compilação em Go, o que encoraja código limpo e sem desperdícios. O escopo de uma variável é determinado por chaves : variáveis declaradas dentro de uma função são locais,

Variáveis em Go: Declaração, Atribuição e Escopo

Uma variável é um local na memória que armazena um valor. Em Go, você declara uma variável antes de usá-la, e a linguagem oferece múltiplas formas de declaração, cada uma com seu propósito específico. A sintaxe mais comum é usar a palavra-chave var, seguida do nome da variável, seu tipo e o valor inicial.

Go é uma linguagem fortemente tipada, o que significa que cada variável possui um tipo específico. Isso oferece segurança em tempo de compilação e facilita a detecção de erros. Veja como declarar variáveis das formas mais comuns:

package main

import "fmt"

func main() {
    // Forma 1: Declaração explícita com tipo
    var idade int
    idade = 25
    fmt.Println("Idade:", idade)

    // Forma 2: Declaração com inicialização
    var nome string = "João"
    fmt.Println("Nome:", nome)

    // Forma 3: Usando := (atribuição curta) - apenas dentro de funções
    email := "joao@example.com"
    fmt.Println("Email:", email)

    // Forma 4: Declaração múltipla
    var (
        cidade string = "São Paulo"
        pais   string = "Brasil"
        ativo  bool   = true
    )
    fmt.Println(cidade, pais, ativo)

    // Forma 5: Múltiplas variáveis em uma linha
    x, y, z := 1, 2, 3
    fmt.Println(x, y, z)
}

O operador := é muito importante em Go e só funciona dentro de funções. Ele declara e atribui um valor simultaneamente, deixando a linguagem inferir o tipo automaticamente. Variáveis não utilizadas causarão erro de compilação em Go, o que encoraja código limpo e sem desperdícios. O escopo de uma variável é determinado por chaves {}: variáveis declaradas dentro de uma função são locais, enquanto variáveis declaradas fora são globais (exportadas se começarem com letra maiúscula).

Constantes: Imutabilidade e Valores em Tempo de Compilação

Uma constante é um valor que não pode ser alterado após sua declaração. Em Go, constantes são avaliadas em tempo de compilação, não em tempo de execução, o que as torna mais eficientes que variáveis para valores fixos. Constantes devem ser atribuídas a um valor que seja conhecido em tempo de compilação.

A declaração de constantes usa a palavra-chave const e segue padrão similar ao de variáveis, mas sem a possibilidade de usar o operador :=. Constantes são especialmente úteis para definir valores imutáveis que representam configurações, limites ou valores matemáticos fixos em sua aplicação.

package main

import "fmt"

func main() {
    // Forma 1: Constante simples
    const Pi = 3.14159
    fmt.Println("Pi:", Pi)

    // Forma 2: Constante com tipo explícito
    const MaxUsuarios int = 100
    fmt.Println("Máximo de usuários:", MaxUsuarios)

    // Forma 3: Múltiplas constantes
    const (
        Vermelho   = 0
        Verde      = 1
        Azul       = 2
        Amarelo    = 3
    )
    fmt.Println("Verde:", Verde)

    // Forma 4: Usando iota para valores sequenciais
    const (
        Pequeno = iota  // 0
        Médio            // 1
        Grande           // 2
        ExtraGrande      // 3
    )
    fmt.Println("Tamanho Médio:", Médio)

    // Tentativa de alterar uma constante causará erro de compilação
    // Pi = 3.14 // ERRO: cannot assign to Pi
}

O iota é um identificador especial que Go oferece para gerar sequências de constantes numeradas automaticamente. Cada vez que aparece em um bloco const, ele incrementa seu valor começando de 0. Isso é particularmente útil para criar enumerações ou conjuntos de valores relacionados que precisam ser únicos e ordenados.

Tipos Primitivos: Os Blocos de Construção do Go

Go possui um conjunto bem definido de tipos primitivos que formam a base para todos os outros tipos de dados. Compreender esses tipos é fundamental para escrever código eficiente e seguro. Os tipos primitivos em Go são divididos em categorias: números inteiros, números de ponto flutuante, booleanos e strings.

Números Inteiros

Os tipos inteiros em Go variam de tamanho e representação. int8, int16, int32 e int64 são inteiros com sinal (podem ser positivos ou negativos), enquanto uint8, uint16, uint32 e uint64 são sem sinal (apenas não-negativos). Os tipos int e uint têm tamanho dependente da arquitetura (32 ou 64 bits). O rune é um alias para int32 usado para caracteres Unicode, e byte é um alias para uint8.

package main

import "fmt"

func main() {
    // Tipos inteiros com sinal
    var a int8 = 127        // Máximo: 127
    var b int16 = 32767     // Máximo: 32767
    var c int32 = 2147483647
    var d int64 = 9223372036854775807

    // Tipo int (tamanho dependente da arquitetura)
    var e int = 42

    // Tipos inteiros sem sinal
    var f uint8 = 255       // Máximo: 255
    var g uint16 = 65535
    var h uint32 = 4294967295
    var i uint64 = 18446744073709551615

    // Tipo uint
    var j uint = 100

    fmt.Println(a, b, c, d, e, f, g, h, i, j)

    // Operações com inteiros
    soma := 10 + 20
    diferenca := 30 - 15
    produto := 5 * 4
    quociente := 20 / 4
    resto := 17 % 5

    fmt.Println("Soma:", soma, "Resto:", resto)
}

Números de Ponto Flutuante

Os tipos float32 e float64 representam números decimais. O float64 é o tipo padrão quando você usa o operador := com um número decimal, pois oferece maior precisão. Operações com ponto flutuante podem sofrer com erros de arredondamento, então cuidado ao comparar valores flutuantes diretamente.

package main

import "fmt"

func main() {
    // Float32 e Float64
    var pi32 float32 = 3.14159
    var pi64 float64 = 3.141592653589793

    // Inferência de tipo (padrão é float64)
    altura := 1.75
    peso := 70.5

    // Operações com ponto flutuante
    media := (altura + peso) / 2
    areaCirculo := pi64 * 5 * 5

    fmt.Println("Pi32:", pi32)
    fmt.Println("Pi64:", pi64)
    fmt.Println("Média:", media)
    fmt.Println("Área do círculo:", areaCirculo)

    // Cuidado com comparações diretas
    resultado := 0.1 + 0.2
    fmt.Println("0.1 + 0.2 =", resultado)
    fmt.Println("É igual a 0.3?", resultado == 0.3) // Provavelmente false!
}

Booleanos

O tipo bool representa valores verdadeiros ou falsos. Em Go, não há conversão automática entre booleanos e inteiros (diferente de C), o que reduz bugs sutis. Operadores lógicos como && (E), || (OU) e ! (NÃO) trabalham com booleanos.

package main

import "fmt"

func main() {
    // Tipo booleano
    ativo := true
    deletado := false

    // Operações booleanas
    resultado1 := true && false   // false
    resultado2 := true || false   // true
    resultado3 := !true           // false

    // Em condições
    idade := 18
    maiorIdade := idade >= 18

    if maiorIdade {
        fmt.Println("É maior de idade")
    }

    fmt.Println("Ativo:", ativo)
    fmt.Println("Deletado:", deletado)
    fmt.Println("Resultado OR:", resultado2)
}

Strings

O tipo string representa uma sequência de caracteres Unicode. Strings em Go são imutáveis, ou seja, não podem ser alteradas após criação. Para modificar uma string, você precisa criar uma nova. Strings são delimitadas por aspas duplas ", enquanto caracteres individuais (runes) usam aspas simples '.

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Strings simples
    saudacao := "Olá, Mundo!"
    nome := "Go"

    // Concatenação
    mensagem := saudacao + " Bem-vindo ao " + nome
    fmt.Println(mensagem)

    // Comprimento da string
    fmt.Println("Comprimento:", len(saudacao))

    // Acessando caracteres (índice começa em 0)
    primeiroCodigo := saudacao[0]
    fmt.Println("Primeiro byte:", primeiroCodigo) // Imprime o código ASCII

    // Strings multilinha usando acento grave
    texto := `Este é um
    texto que ocupa
    múltiplas linhas`
    fmt.Println(texto)

    // Funções úteis da package strings
    maiuscula := strings.ToUpper(nome)
    minuscula := strings.ToLower(saudacao)
    contém := strings.Contains(saudacao, "Mundo")

    fmt.Println("Maiúscula:", maiuscula)
    fmt.Println("Contém 'Mundo':", contém)
}

Inferência de Tipos: Deixando Go Descobrir

A inferência de tipos é um recurso poderoso de Go que permite que o compilador determine automaticamente o tipo de uma variável sem que você o especifique explicitamente. Isso torna o código mais conciso sem sacrificar a segurança de tipos. Quando você usa o operador := ou inicializa uma variável com var sem especificar o tipo, Go examina o valor atribuído e infere o tipo apropriado.

O mecanismo de inferência em Go é inteligente: se você atribuir um número inteiro, Go infere int (na arquitetura atual); se for um número decimal, infere float64; se for texto, infere string. Essa inferência acontece em tempo de compilação, não em tempo de execução, então não há custo de performance. É importante notar que a inferência funciona apenas na inicialização; uma vez que uma variável recebe um tipo, ela permanece com esse tipo.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // Inferência com :=
    numero := 42           // int
    decimal := 3.14        // float64
    texto := "Go"          // string
    ativo := true          // bool

    // Função reflect.TypeOf para verificar o tipo inferido
    fmt.Println("Tipo de 'numero':", reflect.TypeOf(numero))
    fmt.Println("Tipo de 'decimal':", reflect.TypeOf(decimal))
    fmt.Println("Tipo de 'texto':", reflect.TypeOf(texto))
    fmt.Println("Tipo de 'ativo':", reflect.TypeOf(ativo))

    // Inferência com var (tipo será inferido do valor)
    var preco = 99.99
    fmt.Println("Tipo de 'preco':", reflect.TypeOf(preco))

    // Inferência em operações
    resultado := numero + 8  // resultado é int, pois ambos são int
    fmt.Println("Tipo de 'resultado':", reflect.TypeOf(resultado))

    // Cuidado: inferência em operações com tipos diferentes requer conversão
    // decimalComInteiro := decimal + numero  // ERRO: tipos diferentes

    // Conversão explícita necessária
    decimalComInteiro := decimal + float64(numero)
    fmt.Println("Tipo de 'decimalComInteiro':", reflect.TypeOf(decimalComInteiro))
    fmt.Println("Valor:", decimalComInteiro)
}

Uma limitação importante da inferência é que você não pode usar := no escopo global (fora de funções); nesse caso, deve usar var com tipo explícito ou com inferência. Além disso, ao trabalhar com constantes, a inferência também funciona, mas a constante mantém um tipo "não-tipado" até ser usada em contextos que exigem um tipo específico, oferecendo maior flexibilidade.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // Constantes não-tipadas permitem operações entre tipos
    const x = 10
    const y = 3.14

    // Uma constante inteira pode ser usada onde float é esperado
    resultado := float64(x) + y
    fmt.Println(resultado)
    fmt.Println("Tipo de 'resultado':", reflect.TypeOf(resultado))

    // Inferência com múltiplas atribuições
    a, b, c := 1, 2.5, "hello"
    fmt.Println("Tipos:", reflect.TypeOf(a), reflect.TypeOf(b), reflect.TypeOf(c))

    // Conversão explícita entre tipos
    inteiro := int(2.99)  // Trunca para 2
    flutuante := float64(inteiro)
    stringDoInteiro := fmt.Sprintf("%d", inteiro)

    fmt.Println("Inteiro:", inteiro)
    fmt.Println("Flutuante:", flutuante)
    fmt.Println("String:", stringDoInteiro)
}

Referências


Artigos relacionados