Enums em Go: forma idiomática e mágica por baixo dos panos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que Go não diz "enum"?

\n\n

Se você veio para Go vindo de Python, Java ou C#, provavelmente teve um leve choque cultural: em Go não existe a palavra-chave enum. Sem enumerações embutidas com numeração automática, sem métodos .name() ou .valueOf(). Apenas você, o compilador e… constantes.

\n\n

A pergunta "What is an idiomatic way of representing enums in Go?" é uma das mais populares no Stack Overflow na seção Golang (milhares de visualizações). E isso não é por acaso. Todo iniciante mais cedo ou mais tarde se depara com um obstáculo: "Como faço um enum de forma que seja ao estilo Go?".

\n\n

Neste artigo, não vamos apenas dar a resposta. Vamos olhar por baixo dos panos: como Go lida com constantes, o que é iota e por que o compilador não considera dois valores int diferentes como tipos diferentes. E no final, você aprenderá como tornar um enum seguro, conveniente e elegante.

\n\n

O problema: por que constantes simples são ruins?

\n\n

A primeira coisa que vem à mente é declarar algumas constantes:

\n\n
const (\n    StatusActive   = 1\n    StatusInactive = 2\n    StatusPending  = 3\n)
\n\n

Até funciona. Mas há um detalhe: qualquer função que aceite int engolirá o valor 999 ou -42. O compilador não dirá uma palavra. Isso não é uma enumeração — são apenas números com nomes.

\n\n

Um enum de verdade deve:

\n
    \n
  • Limitar o conjunto de valores permitidos;
  • \n
  • Ser type-safe (não se pode passar um int onde se espera um status);
  • \n
  • Ser legível e facilmente extensível.
  • \n
\n\n

Go resolve esse problema de forma idiomática: através de tipos personalizados e iota.

\n\n

Forma idiomática: tipo + iota

\n\n

Aqui está como se parece a resposta canônica daquele Stack Overflow:

\n\n
type Status int\n\nconst (\n    StatusActive   Status = iota // 0\n    StatusInactive               // 1\n    StatusPending                // 2\n    StatusDeleted                // 3\n)
\n\n

O que aconteceu aqui?

\n
    \n
  • type Status int — criamos um novo tipo nomeado. Sim, por baixo dos panos é um int, mas para o compilador, Status e int são coisas diferentes.
  • \n
  • iota — é um gerador de inteiros sequenciais que funciona apenas dentro de um bloco const. Cada nova linha incrementa o valor em 1.
  • \n
\n\n

Agora, uma função que aceita Status não compilará se você tentar passar um int comum:

\n\n
func Process(s Status) {\n    // ...\n}\n\nfunc main() {\n    Process(StatusActive) // OK\n    Process(1)            // Erro de compilação! Cannot use 1 (type int) as type Status\n}
\n\n

Isso é type-safety — a primeira e principal razão para usar tipos personalizados para enums.

\n\n

Por que iota em vez de numeração manual?

\n\n

Imagine que você adicionou um novo status no meio da lista. Com numeração manual, você teria que reescrever todos os valores subsequentes. iota faz isso automaticamente. Isso não é apenas conveniente — é idiomático.

\n\n

Por baixo dos panos: como Go vê o enum

\n\n

Vamos entender o que realmente acontece na fase de compilação.

\n\n

1. iota não é runtime

\n\n

iota não existe durante a execução do programa. É uma ferramenta de pré-processamento (mais precisamente, parte do compilador de pacotes). Na fase de compilação, todos os iota são substituídos por números específicos. No binário resultante, você verá apenas 0, 1, 2.

\n\n

Você pode verificar isso com go tool compile ou apenas olhando o código assembly:

\n\n
// Código fonte\nconst (\n    A                

Blogs

Book Recommendations