iota em Go: constantes, enums e máscaras de bits
Como o contador iota preenche blocos const em Go, monta tipos com método String e flags com shift, e por que o número não deve virar dado gravado.
intermediárioPublicada em · 20 min de leitura
Em Go, iota é um contador que vive dentro de um bloco const e sobe um número a cada linha. Parece detalhe de sintaxe, mas é o mecanismo que a linguagem usa para numerar enumerações, montar flags de bits e definir tamanhos de dados como KB, MB e GB. Esta aula começa pelo funcionamento mais simples, passa pelas regras que pegam quem está começando e termina no erro mais caro: deixar o número de uma escapar do programa e virar dado gravado.
Antes de ler, convém saber o que é uma constante e como os tipos inteiros funcionam, que estão em Conversão de tipos. Os exemplos de JSON usam tags de struct, explicadas em Tags.
Mapa da aula
| Parte | Seções | O que você vai aprender |
|---|---|---|
| Mecanismo | 1 a 3 | O que é o contador, a regra de repetição da expressão e como pular valores com _ |
| Uso prático | 4 a 6 | Enumerações com tipo próprio e método String, máscaras de bits com shift e expressões como KB, MB e GB |
| Cuidado com o número | 7 e 8 | Por que o valor numérico não deve virar contrato de API, e os erros que mais aparecem |
| Prática | 9 e 10, glossário, fontes e relacionados | Exercícios com resposta de referência, o que ficou sem confirmação e onde conferir |
1. O que é o
Pense numa catraca de metrô. Ela não sabe quem você é, mas conta quantas pessoas passaram, e cada passagem soma um ao total. Em Go, o bloco const faz o papel da fila de catracas: cada linha dele é uma passagem, e iota é o número que a catraca mostra naquela linha. Com uma diferença importante: a catraca anda quando você escreve a linha, e não quando você usa a constante depois. A contagem fica congelada no código.
Tecnicamente, iota é um identificador pré-declarado que só tem sentido dentro de uma declaração de constante. Em cada linha, ele vale o índice da linha no bloco, começando em zero. A especificação da linguagem chama cada linha de especificação de constante, ou , e é por ela que a contagem avança. O valor de iota é um inteiro sem tipo, então a constante resultante assume o tipo que o uso exigir. Esse detalhe volta na seção 6.
Para que isso serve? Sem iota, uma lista de dias da semana exige escrever Domingo = 0, Segunda = 1 e assim por diante. Com sete dias isso é tolerável. Com trinta valores, vira uma planilha digitada à mão, e o erro clássico é repetir um número sem que nada reclame, porque o Go aceita duas constantes com o mesmo valor. Com iota, a sequência nasce da posição da linha, e não da digitação.
package main
import "fmt"
const (
Domingo = iota // 0
Segunda // 1
Terca // 2
)
func main() {
fmt.Println(Domingo, Segunda, Terca)
}
A saída do programa:
0 1 2
A primeira linha vale zero porque é a linha de índice zero. As duas seguintes não escrevem iota em lugar nenhum e, mesmo assim, recebem 1 e 2. A explicação está na próxima seção.
2. A regra de repetição
Quando uma linha não traz expressão, o Go repete a última expressão não vazia do bloco. A especificação chama isso de . A expressão é reaplicada em cada linha, e como o iota avança a cada linha, a mesma expressão produz um resultado diferente em cada uma.
package main
import "fmt"
const (
A = 10 * iota // iota = 0
B // iota = 1, repete 10 * iota
C // iota = 2, repete 10 * iota
)
const (
X, Y = iota, iota * 100 // iota = 0 para os dois nomes
Z, W // iota = 1 para os dois nomes
)
func main() {
fmt.Println("A, B, C:", A, B, C)
fmt.Println("X, Y, Z, W:", X, Y, Z, W)
}
A, B, C: 0 10 20
X, Y, Z, W: 0 0 1 100
Leia a saída linha por linha. A vale 10 * 0, então zero. B herda 10 * iota com iota valendo 1, e dá 10. C dá 20. O segundo bloco mostra outro detalhe: em X, Y = iota, iota * 100 há um único iota por linha, e os dois nomes dessa linha veem o mesmo valor. Por isso X e Y saem com 0 e 0. Na linha seguinte, Z e W herdam as duas expressões com iota valendo 1, e saem com 1 e 100.
O ponto que mais confunde é achar que cada nome incrementa o contador. Quem incrementa é a linha. Um contador que conta linhas e não nomes é o tipo de detalhe que só aparece quando você erra, então vale fixar esta regra antes de seguir.
3. Pulando valores com _
Às vezes a posição zero tem outro significado. Um status 0 como “nenhum” não deve ser confundido com um status válido, e uma enumeração pode começar melhor em 1. Para isso existe o identificador vazio _. Ele descarta o valor e o Go não cria nome nenhum, mas a linha continua contando: quem gasta o índice é a linha, e não o nome.
package main
import "fmt"
const (
A = 10 * iota // iota = 0
B // iota = 1
_ // iota = 2, linha descartada
C // iota = 3
)
const (
_ = iota // descarta o zero
Primeiro
Segundo
Terceiro
)
func main() {
fmt.Println("A, B, C:", A, B, C)
fmt.Println("Primeiro, Segundo, Terceiro:", Primeiro, Segundo, Terceiro)
}
A, B, C: 0 10 30
Primeiro, Segundo, Terceiro: 1 2 3
No primeiro bloco, a linha _ fica no meio e ocupa o índice 2. Por isso C sai com 10 * 3, ou seja, 30, e não 20. No segundo bloco, _ = iota consome o zero e Primeiro começa em 1. Escrever _ = iota e depois só os nomes é uma forma direta de deixar o zero de fora.
A figura a seguir mostra essa sequência do primeiro bloco: o índice que cada linha recebe, a expressão que ela escreve ou herda e o valor que sobra.
Siga a figura da esquerda para a direita. O iota alimenta as quatro linhas, e a seta roxa mostra o índice que cada uma recebe. As caixas laranja são as linhas escritas no código, as verdes são os valores finais, e a linha tracejada é a que não gera nome, por isso não tem seta para a direita.
4. Enumerações com tipo próprio e String
Go não tem a palavra-chave enum. O padrão é declarar um tipo com base inteira e um bloco de constantes que usam esse tipo. A vantagem aparece na assinatura das funções: uma função que recebe um Status diz o que espera, em vez de aceitar qualquer int.
package main
import "fmt"
type Status int
const (
Pendente Status = iota
Pago
Cancelado
)
func (s Status) String() string {
switch s {
case Pendente:
return "pendente"
case Pago:
return "pago"
case Cancelado:
return "cancelado"
}
return fmt.Sprintf("Status(%d)", int(s))
}
func main() {
s := Pago
fmt.Println("Println usa String:", s)
fmt.Printf("%%v usa String: %v\n", s)
fmt.Printf("%%d ignora String: %d\n", s)
fmt.Println("valor fora da lista:", Status(5))
var n int = int(Cancelado)
fmt.Println("convertido para int:", n)
}
Println usa String: pago
%v usa String: pago
%d ignora String: 1
valor fora da lista: Status(5)
convertido para int: 2
O Println encontrou o método String e imprimiu o nome. O %v faz o mesmo, mas o %d ignora o método e imprime o número. Isso vale para qualquer tipo que tenha String: o pacote fmt procura o método e o usa quando ele existe.
Dois detalhes merecem atenção. Primeiro, o tipo não impede valores fora da lista: Status(5) compila e imprime Status(5), porque o switch tem um retorno para o caso que não foi previsto. Segundo, a conversão de Status para int é explícita, então um número solto não vira Status sem que alguém escreva isso no código.
Gerando o com
Escrever o switch à mão funciona com três valores. Com trinta, vira uma lista que precisa ser mantida em dois lugares do código. O stringer, do pacote golang.org/x/tools, lê o bloco e gera o método. A chamada fica num arquivo com a diretiva go:generate:
package main
import "fmt"
//go:generate go run golang.org/x/tools/cmd/stringer -type=Status
type Status int
const (
Pendente Status = iota
Pago
Cancelado
)
func main() {
fmt.Println(Pago, Cancelado)
}
Os comandos usados na consulta foram go get golang.org/x/tools/cmd/stringer@latest, seguido de go generate ./... e go run .. A saída do programa:
Pago Cancelado
O arquivo gerado, status_string.go, guarda os nomes em uma só string e usa um vetor de índices para cortá-la. O comentário no começo dele diz que um erro de índice inválido na compilação indica que os valores mudaram e pede para rodar o stringer de novo.
A biblioteca padrão usa o mesmo recurso. No pacote time, Weekday começa em iota, então domingo vale 0, e Month começa em 1 + iota, para que janeiro valha 1, como no calendário. A definição está no arquivo src/time/time.go da toolchain.
5. Máscaras de bits com shift
Uma flag é uma opção que está ligada ou desligada. Se cada opção ocupa um bit diferente, um só número guarda o conjunto inteiro. O << é o que cria esses bits: 1 << n coloca um bit ligado na posição n, então 1 << 0 vale 1 (0001), 1 << 1 vale 2 (0010) e assim por diante. Se o operador de shift ainda é novidade, a nota Left shift mostra o movimento com exemplos.
package main
import "fmt"
type Permissao uint8
const (
Ler Permissao = 1 << iota // 1 << 0 = 0001
Escrever // 1 << 1 = 0010
Executar // 1 << 2 = 0100
Admin // 1 << 3 = 1000
)
func main() {
p := Ler | Executar
fmt.Printf("Ler|Executar = %d (%04b)\n", p, p)
fmt.Println("tem escrita?", p&Escrever != 0)
p |= Escrever
fmt.Printf("depois de |= Escrever: %04b\n", p)
p &^= Executar
fmt.Printf("depois de &^= Executar: %04b\n", p)
fmt.Printf("Admin = %d (%04b)\n", Admin, Admin)
fmt.Printf("1 << 7 = %d\n", 1<<7)
}
Ler|Executar = 5 (0101)
tem escrita? false
depois de |= Escrever: 0111
depois de &^= Executar: 0011
Admin = 8 (1000)
1 << 7 = 128
Três operações fazem o trabalho. O | (OR) liga os bits de dois valores, então Ler | Executar dá 0101. O & (AND) deixa só os bits em comum, e o teste p&Escrever != 0 fica verdadeiro quando o bit de escrita está ligado. Go coloca o & no mesmo nível da multiplicação, acima da comparação, então a expressão se lê como (p & Escrever) != 0 sem parênteses. O terceiro operador é o &^ (AND NOT), que desliga um bit: p &^= Executar tira a execução e deixa o resto como estava.
Permissao é um uint8, então cabem oito flags. A linha Admin vale 8 porque é a quarta do bloco, e 1 << 3 é 1000 em binário. Uma nona flag exigiria outro tipo, porque o valor não cabe em 8 bits.
A figura mostra o caminho completo. As quatro constantes da coluna da esquerda são as potências de dois. Só Ler e Executar entram na combinação, e as setas laranja as ligam ao operador OR. O resultado p, com 0101, aparece em verde porque é o dado, e o teste com & fica embaixo, mostrando que Escrever não está ligado. Escrever e Admin existem no bloco, mas não participam desta combinação, por isso não recebem seta.
6. iota com expressões: KB, MB e GB
O mesmo mecanismo serve para expressões que usam o valor de iota, e não só para contar. Um exemplo clássico são as unidades de dados: um KB tem 1024 bytes, um MB tem 1024 KB, e assim por diante. Cada unidade vale 1024 vezes a anterior, ou seja, 2 elevado a 10 vezes a posição. A expressão 1 << (10 * iota) gera a progressão inteira.
package main
import "fmt"
const (
_ = iota
KB = 1 << (10 * iota)
MB
GB
TB
)
func main() {
fmt.Println(KB, MB, GB, TB)
var tamanho int64 = 3 * GB
fmt.Println("3 GB em bytes:", tamanho)
}
1024 1048576 1073741824 1099511627776
3 GB em bytes: 3221225472
O _ = iota consome o zero. Assim, KB usa iota igual a 1 e vale 1 << 10. Com iota igual a zero, a conta daria 1, que é um byte e não um KB. As constantes numéricas da especificação têm precisão exata e não estouram. O estouro acontece quando o valor é atribuído a um tipo que não comporta a conta.
package main
import "fmt"
const (
_ = iota
KB = 1 << (10 * iota)
MB
GB
TB
PB
EB
)
func main() {
var pequeno int32 = TB
fmt.Println(pequeno)
}
A compilação falha com:
# iota-aula/ex05b-erro-tipo
ex05b-erro-tipo/main.go:16:22: cannot use TB (untyped int constant 1099511627776) as int32 value in variable declaration (overflows)
O compilador recusa a linha porque 1 TB não cabe em int32. O erro sai antes de o programa rodar, e a mensagem diz qual constante e qual tipo causaram o problema. O estouro aparece na compilação, e não em produção.
7. Não trate o número como contrato
O risco mais caro de iota não aparece no compilador. Ele aparece quando o número sai do programa, para um banco, um JSON, um arquivo ou outro serviço. Um valor gravado como 1 já é dado, e quem o lê depois depende da ordem das linhas que existia no código da época.
package main
import (
"encoding/json"
"fmt"
)
// Versão 1 do pacote: a ordem original.
type StatusV1 int
const (
PendenteV1 StatusV1 = iota
PagoV1
CanceladoV1
)
// Versão 2: alguém inseriu "Rascunho" no começo do bloco.
type StatusV2 int
const (
RascunhoV2 StatusV2 = iota
PendenteV2
PagoV2
CanceladoV2
)
type RegistroV1 struct {
ID int `json:"id"`
Status StatusV1 `json:"status"`
}
type RegistroV2 struct {
ID int `json:"id"`
Status StatusV2 `json:"status"`
}
func main() {
// A versão 1 grava um pedido pago: o número 1.
gravado, _ := json.Marshal(RegistroV1{ID: 42, Status: PagoV1})
fmt.Println("gravado pela v1:", string(gravado))
// A versão 2 lê o mesmo registro antigo.
var lido RegistroV2
if err := json.Unmarshal(gravado, &lido); err != nil {
fmt.Println("erro:", err)
return
}
fmt.Println("lido pela v2, status numérico:", lido.Status)
fmt.Println("significado na v2:", map[StatusV2]string{
RascunhoV2: "rascunho", PendenteV2: "pendente", PagoV2: "pago", CanceladoV2: "cancelado",
}[lido.Status])
}
gravado pela v1: {"id":42,"status":1}
lido pela v2, status numérico: 1
significado na v2: pendente
A versão 1 gravou o número 1, que significava Pago. A versão 2 inseriu Rascunho no começo, e o mesmo 1 passou a significar Pendente. Nenhum erro foi escrito: o programa rodou e o status apareceu com outro nome. Esse é o tipo de bug que passa despercebido, porque nada falha de forma visível. Só um teste que lê um registro antigo acusaria o problema.
Leia a figura de cima para baixo em cada coluna. Na versão 1, à esquerda, o número 1 aponta para Pago, e a seta verde mostra a leitura correta. Na versão 2, à direita, o mesmo registro guardado com 1 aponta para Pendente, e a seta rosa marca a leitura errada. O registro no centro não mudou, só a ordem do código.
Há duas saídas, e elas se somam. A primeira é dar valor explícito a cada nome, para que a posição deixe de importar. A segunda é gravar o nome, e não o número. Com MarshalText e UnmarshalText, o encoding/json escreve "pago" e lê de volta, e um nome desconhecido vira erro em vez de um número qualquer. A documentação de encoding/json diz que, sem MarshalJSON, o Marshal chama MarshalText quando o valor implementa encoding.TextMarshaler.
package main
import (
"encoding/json"
"fmt"
)
type Status int
// Valores fixos: cada nome tem um número que não depende da posição no bloco.
const (
Pendente Status = 0
Pago Status = 1
Cancelado Status = 2
Rascunho Status = 3 // novo, no fim do bloco
)
var nomes = map[Status]string{
Pendente: "pendente",
Pago: "pago",
Cancelado: "cancelado",
Rascunho: "rascunho",
}
var valores = map[string]Status{
"pendente": Pendente,
"pago": Pago,
"cancelado": Cancelado,
"rascunho": Rascunho,
}
// MarshalText faz o JSON gravar o nome, não o número.
func (s Status) MarshalText() ([]byte, error) {
n, ok := nomes[s]
if !ok {
return nil, fmt.Errorf("status desconhecido: %d", int(s))
}
return []byte(n), nil
}
// UnmarshalText lê o nome de volta. Nome desconhecido vira erro, não um número qualquer.
func (s *Status) UnmarshalText(texto []byte) error {
v, ok := valores[string(texto)]
if !ok {
return fmt.Errorf("status desconhecido: %q", texto)
}
*s = v
return nil
}
type Registro struct {
ID int `json:"id"`
Status Status `json:"status"`
}
func main() {
gravado, _ := json.Marshal(Registro{ID: 42, Status: Pago})
fmt.Println("gravado:", string(gravado))
var lido Registro
if err := json.Unmarshal([]byte(`{"id":42,"status":"cancelado"}`), &lido); err != nil {
fmt.Println("erro:", err)
}
fmt.Println("lido:", lido.Status == Cancelado, lido.Status)
var ruim Registro
err := json.Unmarshal([]byte(`{"id":7,"status":"estornado"}`), &ruim)
fmt.Println("nome inválido:", err)
}
gravado: {"id":42,"status":"pago"}
lido: true 2
nome inválido: status desconhecido: "estornado"
A linha lido imprime 2 porque esta versão não tem método String. O nome só aparece quando ele existe, como na seção 4. Com as duas medidas, acrescentar uma constante no fim do bloco deixa os dados antigos intactos, e o JSON continua legível para quem abre o banco.
8. Erros comuns e boas práticas
Os casos abaixo cobrem o que costuma dar errado com iota, e cada um traz o motivo.
Achar que o primeiro valor é 1
Todo mundo tropeça no zero uma vez. A contagem de índices começa em zero, e iota herda isso sem pedir licença. Se o zero precisa ficar de fora, use _ = iota, como na seção 3. Zero é um valor válido como qualquer outro, e só parece errado para quem esperava outro número.
Achar que cada nome avança o contador
Em X, Y = iota, iota * 100, os dois nomes veem o mesmo iota. A seção 2 mostra o resultado: X e Y saem com 0. Quem avança o contador é a linha. Se você espera que Y seja 1, a expectativa está na linha errada.
Repetir uma expressão que não usa iota
A linha sem expressão repete a última expressão, mesmo quando ela é um número fixo. O exemplo abaixo mostra o efeito.
package main
import "fmt"
const (
A = 5
B
C
)
const (
D = iota * 5
E
F = 7
G
)
func main() {
fmt.Println("A, B, C:", A, B, C)
fmt.Println("D, E, F, G:", D, E, F, G)
}
A, B, C: 5 5 5
D, E, F, G: 0 5 7 7
B e C valem 5, porque repetem A = 5, e G vale 7, porque repete F = 7, e não iota * 5. A regra copia a expressão e reaplica, então uma expressão sem iota gera o mesmo valor em todas as linhas seguintes.
Tratar o tipo como validação
Um tipo Status organiza o código, mas não valida dados. Status(5) passou pelo compilador na seção 4. Quando o valor vem de fora, como JSON, formulário ou fila, a validação precisa estar na leitura, e o UnmarshalText da seção 7 faz isso para nomes desconhecidos.
Boas práticas
Na prática, as regras das seções anteriores viram um conjunto pequeno:
- Declare um tipo próprio para cada enum. O motivo é que a assinatura da função passa a dizer o que aceita.
- Dê valor explícito a qualquer constante que seja gravada ou enviada para fora. O motivo é que a posição deixa de importar.
- Acrescente constantes novas no fim do bloco. O motivo é que os valores antigos continuam os mesmos.
- Implemente
String, à mão ou comstringer, para que logs e mensagens mostrem o nome. O motivo é que%de%vmostram coisas diferentes. - Grave o nome, e não o número, e rejeite nome desconhecido na leitura. O motivo é que a seção 7 mostra o que acontece quando o número muda.
9. Exercícios
Os exercícios usam o que foi rodado acima. Tente resolver antes de olhar a resposta, e confira depois com o programa.
Exercícios 1 e 2: dias da semana e permissões
Exercício 1: escreva um bloco const com os dias da semana de segunda (1) a domingo (7), com tipo Dia, sem digitar nenhum número à mão. Exercício 2: a partir das permissões da seção 5, adicione Deletar sem alterar os valores das anteriores, e faça o String listar as permissões ativas, como ler|executar. Explique por que Deletar entra no fim.
Resposta de referência. No exercício 1, Segunda Dia = iota + 1 faz a contagem começar em 1. No exercício 2, Deletar vem depois de Admin, então Ler, Escrever, Executar e Admin mantêm os valores 1, 2, 4 e 8. Se Deletar fosse inserida antes de Admin, Admin passaria a valer 16, e qualquer dado gravado com 8 mudaria de significado.
package main
import (
"fmt"
"strings"
)
// Exercício 1: dias de segunda (1) a domingo (7), sem escrever números à mão.
type Dia int
const (
Segunda Dia = iota + 1
Terca
Quarta
Quinta
Sexta
Sabado
Domingo
)
// Exercício 2: permissões com um String que lista as flags ativas.
type Permissao uint8
const (
Ler Permissao = 1 << iota
Escrever
Executar
Admin
Deletar // novo: entrou no fim, os valores anteriores não mudam
)
func (p Permissao) String() string {
nomes := []string{}
for _, f := range []struct {
bit Permissao
nome string
}{
{Ler, "ler"}, {Escrever, "escrever"}, {Executar, "executar"},
{Admin, "admin"}, {Deletar, "deletar"},
} {
if p&f.bit != 0 {
nomes = append(nomes, f.nome)
}
}
if len(nomes) == 0 {
return "nenhuma"
}
return strings.Join(nomes, "|")
}
func main() {
fmt.Println("segunda =", Segunda, "| domingo =", Domingo)
fmt.Println("Ler|Executar:", Ler|Executar)
fmt.Println("Deletar =", Deletar, "| Ler|Deletar:", Ler|Deletar)
fmt.Println("nenhuma:", Permissao(0))
}
segunda = 1 | domingo = 7
Ler|Executar: ler|executar
Deletar = deletar | Ler|Deletar: ler|deletar
nenhuma: nenhuma
Exercício 3: proteger o contrato
Um colega inseriu Rascunho no começo do bloco de Status, como na seção 7. Explique o que quebra nos dados já gravados e escreva uma versão que aceite essa inserção sem mudar a leitura de registros antigos.
Resposta de referência. O que quebra é o número 1 gravado antes, que passa a ser lido como Pendente, porque a posição de Pago mudou. A correção combina valor explícito com gravação por nome. Como Rascunho vem no fim, os números antigos continuam iguais, e com MarshalText o registro gravado nem depende do número. O programa é o da seção 7.
10. O que não foi confirmado
Algumas coisas ficaram fora do que pude conferir nesta revisão:
- Histórico por versão do Go. A aula descreve a especificação da toolchain go1.27.2 instalada em 2026-11-03. Não verifiquei em que versão cada detalhe apareceu.
- Plataforma. Os exemplos rodaram só em darwin/arm64. A linha
fmt.Println(KB, MB, GB, TB)da seção 6 passa as constantes paraint, cujo tamanho depende da plataforma. Em uma arquitetura de 32 bits,TBnão caberia nesseinte o programa não compilaria. Não testei esse caso. - Versão do stringer. Usei
golang.org/x/tools/cmd/stringerna versão v0.52.0, resolvida na data da consulta. Não testei outras versões. - Comentário do arquivo gerado. A frase sobre o erro de índice foi lida no próprio
status_string.go. Não encontrei na documentação oficial uma descrição com essas palavras. - Outros formatos. A seção 7 usa
encoding/json. Protobuf, ORMs e outros formatos podem ter regras próprias para enums, e não foram verificados aqui.
Glossário
- iota: identificador pré-declarado que vale o índice da linha dentro de um bloco const.
- constante: nome ligado a um valor fixo, decidido na compilação.
- bloco const: grupo de declarações de constante entre parênteses.
- ConstSpec: cada linha de declaração dentro de um bloco const.
- constante sem tipo: constante que assume o tipo do uso, com precisão exata.
- repetição implícita: linha sem expressão que recebe a última expressão não vazia do bloco.
- enum: conjunto de valores nomeados de um tipo, feito em Go com tipo próprio e constantes.
- máscara de bits, ou flags: número em que cada bit representa uma opção ligada ou desligada.
- shift, ou deslocamento: operadores
<<e>>, que movem os bits de um número. - método String: método que o pacote
fmtusa para imprimir um valor como texto. - stringer: ferramenta que gera o método String a partir dos nomes do bloco.
- MarshalText e UnmarshalText: métodos que convertem um valor para texto e de volta, usados pelo
encoding/json.
Fontes
Consultadas em 2026-11-03, com go1.27.2 darwin/arm64 (go version).
- The Go Programming Language Specification, seções Constants, Iota, Representability e Operator precedence. Consultada pela cópia local da toolchain (
go env GOROOT, arquivodoc/go_spec.html) e publicada em https://go.dev/ref/spec. A página pública pode ser de versão mais nova que a instalada. - Pacote time, tipos Month e Weekday. Consultado no arquivo
src/time/time.goda toolchain. Documentação pública: https://pkg.go.dev/time. - Pacote encoding/json, comentário da função Marshal sobre encoding.TextMarshaler. Consultado no arquivo
src/encoding/json/encode.goda toolchain. Documentação pública: https://pkg.go.dev/encoding/json. - golang.org/x/tools/cmd/stringer, versão v0.52.0 resolvida por
go get ... @latestem 2026-11-03. Documentação: https://pkg.go.dev/golang.org/x/tools/cmd/stringer.
Relacionados
- Trilha de Go: mapa da trilha, em que esta aula é a 1.3.
- Left shift: o operador
<<usado nas máscaras de bits. - Conversão de tipos: tipos e conversões, pré-requisito das constantes tipadas.
- Tags: tags de struct, usadas no JSON da seção 7.
- Structs: campos de struct, base do registro serializado.
- Go: visão geral da linguagem.