Pular para o conteúdo

Trilha de Go · parte 2 de 65

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

Tamanho da fonte:
Largura da coluna:
Tom:
Espaço entre linhas:
Fonte:

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

ParteSeçõesO que você vai aprender
Mecanismo1 a 3O que é o contador, a regra de repetição da expressão e como pular valores com _
Uso prático4 a 6Enumeraçõ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úmero7 e 8Por que o valor numérico não deve virar contrato de API, e os erros que mais aparecem
Prática9 e 10, glossário, fontes e relacionadosExercí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.

O iota sobe uma unidade por linha do bloco const; a linha sem expressão repete a anterior, e a linha com _ gasta um índice sem criar nome.
figura 1 · Como o iota conta as linhas de um

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.

Constantes 1 << iota geram 0001, 0010, 0100 e 1000; combinar Ler e Executar dá 0101, e o teste com AND mostra que Escrever está desligado.
figura 2 · Máscara de bits: shift cria as flags, | liga e & testa

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.

Um enum com Pendente igual a 0 e Pago igual a 1 lê o número 1 como Pago; depois de inserir Rascunho no começo, o mesmo 1 passa a ser Pendente.
figura 3 · Trocar a ordem do muda o número guardado

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 com stringer, para que logs e mensagens mostrem o nome. O motivo é que %d e %v mostram 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 para int, cujo tamanho depende da plataforma. Em uma arquitetura de 32 bits, TB não caberia nesse int e o programa não compilaria. Não testei esse caso.
  • Versão do stringer. Usei golang.org/x/tools/cmd/stringer na 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 fmt usa 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, arquivo doc/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.go da 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.go da 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 ... @latest em 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.

↑ Voltar ao topo

Atalhos de teclado

Abrir busca
/
Alternar tema claro/escuro
t
Aula anterior na série
←
Próxima aula na série
→
Lista de atalhos
?