Uma constante é um valor fixado em tempo de compilação. Declare com const, e o Go recusa qualquer tentativa de alterá-la.
Constantes podem ser declaradas no nível de pacote ou dentro de uma função, uma por linha ou agrupadas em um bloco const ( ... ). MB e GB são calculadas a partir de outras constantes, e o compilador faz essa conta uma vez só, no build.
Atribuir a uma constante, como em MaxRetries = 5, é erro de compilação:
./main.go:8:2: cannot assign to MaxRetries (neither addressable nor a map index expression)
O que pode ser uma constante
Só três tipos de valores: booleanos, números (inteiros, floats, números complexos e runes) e strings. O valor precisa ser calculável pelo compilador, ou seja, literais, outras constantes, aritmética entre eles e algumas funções embutidas, como len de uma string constante.
Tudo o que exige executar código ou alocar memória fica de fora:
const Colors = []string{"red", "green"} // slice
const Started = time.Now() // function call
./main.go:9:16: []string{…} (value of type []string) is not constant
./main.go:10:13: time.Now() (value of struct type time.Time) is not constant
Por que Go não tem slices ou maps constantes
Slices, maps e structs que os contêm são referências a memória que existe em tempo de execução, então o compilador não consegue embuti-los no programa como valores fixos, e Go não tem modificador readonly ou final para variáveis. As alternativas comuns:
A cópia custa uma alocação por chamada, o que não é problema para dados do tamanho de uma configuração. Para buscas por chave, um switch dentro de uma função é outra opção com cara de constante: func statusText(code int) string { switch code { ... } }.
Constantes não tipadas
Uma constante declarada sem tipo é não tipada (untyped). Ela tem uma categoria (inteiro, float, rune, string, bool), mas nenhum tipo Go específico até ser usada, e se adapta ao contexto:
Ratio funciona como int, float64, uint8 e float32 sem conversão. É por isso que time.Sleep(2 * time.Second) compila: 2 é não tipado e vira um time.Duration.
Quando o contexto não exige um tipo, como em x := Ratio, a constante recebe o seu tipo padrão:
| Categoria da constante não tipada | Tipo padrão |
|---|---|
inteiro (42) | int |
ponto flutuante (4.2) | float64 |
rune ('a') | rune (int32) |
complexo (2i) | complex128 |
| string | string |
| booleano | bool |
Aritmética exata
Constantes numéricas não tipadas são exatas. O compilador representa inteiros com pelo menos 256 bits, então valores intermediários podem ser muito maiores do que qualquer tipo Go:
Huge em si não pode ser impresso como inteiro, porque passá-lo para fmt.Println o converte para int, e o compilador percebe isso (float64(Huge) funciona, já que um float comporta essa magnitude):
cannot use Huge (untyped int constant 1267650600228229401496703205376) as int value in argument to fmt.Println (overflows)
A mesma verificação barra erros menores em tempo de compilação, como var b byte = 300:
cannot use 300 (untyped int constant) as byte value in variable declaration (overflows)
Constantes tipadas
Dê um tipo a uma constante e ela passa a se comportar como um valor desse tipo em todo lugar, inclusive na recusa do Go em misturar tipos:
const Limit int = 10
var f float64 = 2
fmt.Println(Limit * f)
invalid operation: Limit * f (mismatched types int and float64)
Com const Limit = 10 (não tipada), Limit * f compila e dá 20. Então deixe as constantes não tipadas, a menos que o tipo tenha significado. Ele tem quando você declara constantes de um tipo nomeado seu, que é como Go constrói enums:
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
)
Aqui o tipo é o objetivo: uma função que recebe um Weekday documenta o que espera. iota e os padrões de enum construídos com ele estão na página de enums e iota.
const ou var
const | var | |
|---|---|---|
| Pode mudar | não | sim |
| Valor conhecido | em tempo de compilação | em tempo de execução |
| Tipos permitidos | bool, numéricos, string | qualquer um |
| Ocupa memória em tempo de execução | não | sim |
Dá para pegar o endereço (&x) | não | sim |
| Sem uso é erro | não | sim, para locais |
Use const para tudo o que é realmente fixo: limites, tamanhos, nomes, códigos de protocolo, strings de formato. Você ganha de graça verificação de overflow em tempo de compilação e aritmética exata.
Armadilhas
Constantes tipadas não se adaptam. const Timeout int = 5 não pode ser passada onde se espera um time.Duration sem conversão; uma const Timeout = 5 não tipada pode ser multiplicada: Timeout * time.Second.
Divisão inteira acontece em constantes inteiras. const Half = 1 / 2 vale 0, porque os dois operandos são inteiros não tipados. Escreva 1.0 / 2 para obter 0.5.
Sem endereço. &MaxRetries não compila. Se uma API pede um *int, copie a constante para uma variável antes.
Perguntas frequentes
Como declarar uma constante em Go?
Com const: const MaxRetries = 3 ou, com tipo explícito, const Timeout time.Duration = 5 * time.Second. Agrupe várias em um bloco: const ( A = 1; B = 2 ). O valor precisa ser calculável em tempo de compilação.
Dá para criar um slice, map ou array constante em Go?
Não. Constantes só podem ser booleanos, números (incluindo runes) e strings. const Colors = []string{"red"} falha com is not constant. Use um var de nível de pacote e, se quem chama não puder alterá-lo, exponha uma função que devolve uma cópia nova.
O que é uma constante não tipada em Go?
Uma constante declarada sem tipo, como const Pi = 3.14159. Ela não tem tipo fixo até ser usada, então a mesma constante funciona como float32, como float64 ou, se o valor permitir, como int. Constantes não tipadas também são exatas: o compilador mantém pelo menos 256 bits de precisão, então const Big = 1 << 100 é válido desde que você só a use em expressões cujo resultado caiba no tipo.
Qual a diferença entre const e var em Go?
Uma const é fixada em tempo de compilação e nunca muda; não ocupa memória em tempo de execução e só pode guardar um bool, um número ou uma string. Uma var é uma posição de memória que pode receber novos valores e guardar qualquer tipo, inclusive valores calculados em tempo de execução, como time.Now().