Constantes são valores imutáveis conhecidos em tempo de compilação. Em Go, elas são declaradas com a palavra-chave const e podem ser de tipos básicos como números, strings e booleanos. Diferente de variáveis, constantes não podem ser alteradas após a declaração e oferecem segurança e previsibilidade ao código. Nesta aula, veremos como declarar constantes, como usar o identificador iota para criar sequências de valores (útil para enums), e a distinção entre constantes tipadas e não tipadas.

Além disso, exploraremos exemplos práticos que demonstram o uso de constantes em configurações, flags e definições de estado, consolidando o entendimento com exercícios e referências.

const

A palavra-chave const é usada para declarar valores constantes. Uma constante pode ser declarada individualmente ou em bloco. A sintaxe básica é:

const Pi = 3.14159
const Greeting = "Olá, mundo"

Constantes podem ser declaradas em bloco, o que é comum quando agrupamos valores relacionados:

const (
    StatusOK = 200
    StatusNotFound = 404
    StatusInternalError = 500
)

Em Go, constantes podem ser não tipadas (sem tipo explícito) ou tipadas. Constantes não tipadas têm maior flexibilidade, pois podem ser usadas em contextos que exigem tipos diferentes, desde que o valor seja compatível. Já as constantes tipadas têm um tipo fixo e só podem ser usadas onde esse tipo é esperado.

Importante: constantes só podem ser de tipos básicos: booleanos, runas, inteiros, ponto flutuante, complexos e strings. Não é possível declarar constantes de structs, slices, maps, funções ou ponteiros.

iota e enums idiomáticos

iota é um identificador pré-declarado que representa inteiros sequenciais. Ele é usado dentro de blocos const para gerar uma série de valores relacionados, tipicamente para enums. A cada linha de declaração dentro do bloco, iota é incrementado em 1, começando em 0.

Exemplo básico:

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

É comum usar iota com expressões para criar padrões como potências de 2, flags de bits, ou valores deslocados. Por exemplo:

const (
    Read = 1 << iota // 1
    Write             // 2
    Execute           // 4
)

Também podemos pular valores com _ (underscore) ou reiniciar a sequência em um novo bloco. iota é uma ferramenta poderosa para definir enums de forma limpa e idiomática em Go.

Constantes tipadas e não tipadas

Constantes não tipadas são declaradas sem um tipo explícito. Elas têm um tipo default (como int ou float64), mas podem ser atribuídas a variáveis de tipos compatíveis sem necessidade de conversão explícita. Por exemplo:

const x = 42          // não tipada (int default)
var i int = x         // ok
var f float64 = x     // ok, x é convertido implicitamente para float64
var b byte = x        // ok, byte é uint8

Constantes tipadas são declaradas com um tipo explícito:

const y int = 42      // tipada como int
var j int = y         // ok
var k float64 = y     // erro: não pode usar y (tipo int) como float64

A flexibilidade das constantes não tipadas é útil para evitar conversões desnecessárias e manter o código mais limpo. No entanto, constantes tipadas garantem que o valor só será usado com o tipo especificado, aumentando a segurança em alguns contextos.

Exemplos

Vamos ver alguns exemplos práticos que combinam os conceitos.

Exemplo 1: Dias da semana com iota

package main

import "fmt"

const (
    Monday = iota
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    Sunday
)

func main() {
    fmt.Println(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
    // Output: 0 1 2 3 4 5 6
}

Exemplo 2: Flags de permissão (bits)

package main

import "fmt"

const (
    Read = 1 << iota
    Write
    Execute
)

func main() {
    fmt.Println(Read, Write, Execute) // 1 2 4
}

Exemplo 3: Constantes tipadas vs não tipadas

package main

import "fmt"

const (
    a = 100          // não tipada
    b int = 100      // tipada
)

func main() {
    var x int = a
    var y float64 = a // ok, a é não tipado
    fmt.Println(x, y)
    // var z float64 = b // erro: não compila
}

Exemplo 4: Enum com valores personalizados

package main

import "fmt"

const (
    _ = iota          // descarta 0
    KB = 1 << (10 * iota) // 1 << 10 = 1024
    MB                 // 1 << 20 = 1048576
    GB                 // 1 << 30 = 1073741824
)

func main() {
    fmt.Println(KB, MB, GB)
}

Boas práticas

Utilize constantes para valores que não mudam, como configurações, erros conhecidos e limites. Prefira constantes não tipadas para maior flexibilidade, a menos que precise de segurança de tipo. Use iota para sequências lógicas, mas evite depender de valores numéricos específicos; se precisar de valores explícitos, atribua-os manualmente.

Referências

Exercícios

  1. Declare um bloco de constantes para os dias da semana usando iota, começando de 1 (segunda = 1).

    ✓ Resposta:
    const (
        Monday = iota + 1
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
        Sunday
    )
  2. Crie constantes para as cores vermelho, verde e azul usando iota, mas com valores 1, 2 e 4 respectivamente (bits).

    ✓ Resposta:
    const (
        Red = 1 << iota
        Green
        Blue
    )
  3. Explique a diferença entre constantes tipadas e não tipadas. Dê um exemplo de cada.

    ✓ Resposta:

    Constantes não tipadas não possuem tipo explícito e podem ser usadas em contextos que exigem tipos diferentes (desde que compatíveis). Constantes tipadas têm tipo fixo e só podem ser atribuídas a variáveis do mesmo tipo.

    const a = 100       // não tipada
    const b int = 100   // tipada
    
    var x int = a       // ok
    var y float64 = a   // ok
    // var z float64 = b // erro
  4. Escreva um programa que defina constantes para os meses do ano usando iota, começando de janeiro = 1.

    ✓ Resposta:
    package main
    
    import "fmt"
    
    const (
        January = iota + 1
        February
        March
        April
        May
        June
        July
        August
        September
        October
        November
        December
    )
    
    func main() {
        fmt.Println(January, February, March, April, May, June, July, August, September, October, November, December)
    }
  5. Crie um bloco de constantes que defina os tamanhos de arquivo: KB, MB, GB, TB, usando iota com deslocamento de bits (1024 base).

    ✓ Resposta:
    const (
        _ = iota
        KB = 1 << (10 * iota) // 1 << 10
        MB                     // 1 << 20
        GB                     // 1 << 30
        TB                     // 1 << 40
    )