Enums en Go: forma idiomática y magia bajo el capó

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿por qué Go no dice «enum»?

\n\n

Si vienes a Go desde Python, Java o C#, seguramente has experimentado un pequeño choque cultural: en Go no existe la palabra clave enum. No hay enumeraciones integradas con numeración automática, ni métodos .name() o .valueOf(). Solo tú, el compilador y… constantes.

\n\n

La pregunta «What is an idiomatic way of representing enums in Go?» es una de las más populares en Stack Overflow en la sección de Golang (miles de visitas). Y no es casualidad. Todo principiante tarde o temprano se topa con un muro: «¿Cómo hago un enum de manera que sea propio de Go?».

\n\n

En este artículo no solo daremos la respuesta. Nos adentraremos bajo el capó: cómo Go maneja las constantes, qué es iota y por qué el compilador no considera dos valores int diferentes como tipos distintos. Y al final, aprenderás cómo hacer un enum seguro, práctico y elegante.

\n\n

El problema: ¿por qué las constantes simples son malas?

\n\n

Lo primero que viene a la mente es declarar varias constantes:

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

Parece que funciona. Pero hay un detalle: cualquier función que acepte int aceptará el valor 999 o -42. El compilador no dirá ni una palabra. Esto no es una enumeración, son solo números con nombre.

\n\n

Un verdadero enum debe:

\n
    \n
  • Limitar el conjunto de valores permitidos;
  • \n
  • Ser seguro en cuanto a tipos (no se puede pasar un int donde se espera un estado);
  • \n
  • Ser legible y fácilmente extensible.
  • \n
\n\n

Go resuelve esta tarea de forma idiomática: mediante tipos personalizados e iota.

\n\n

La forma idiomática: tipo + iota

\n\n

Así es como se ve la respuesta canónica de ese mismo 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

¿Qué ocurrió aquí?

\n
    \n
  • type Status int — creamos un nuevo tipo con nombre. Sí, bajo el capó es int, pero para el compilador Status e int son cosas diferentes.
  • \n
  • iota es un generador de enteros secuenciales que solo funciona dentro de un bloque const. Cada nueva línea incrementa el valor en 1.
  • \n
\n\n

Ahora, una función que acepte Status no se compilará si intentas pasarle un int normal:

\n\n
func Process(s Status) {\n    // ...\n}\n\nfunc main() {\n    Process(StatusActive) // OK\n    Process(1)            // ¡Error de compilación! Cannot use 1 (type int) as type Status\n}
\n\n

Esto es la seguridad de tipos: la primera y principal razón para usar tipos personalizados para los enums.

\n\n

¿Por qué iota y no numeración manual?

\n\n

Imagina que agregas un nuevo estado en medio de la lista. Con numeración manual, tendrías que reescribir todos los valores siguientes. iota lo hace automáticamente. No solo es conveniente, es idiomático.

\n\n

Bajo el capó: cómo ve Go el enum

\n\n

Analicemos qué sucede realmente en la etapa de compilación.

\n\n

1. iota no es runtime

\n\n

iota no existe durante la ejecución del programa. Es una herramienta de preprocesamiento (más precisamente, parte del compilador de paquetes). En la etapa de compilación, todos los iota se reemplazan por números concretos. En el binario resultante, solo verás 0, 1, 2.

\n\n

Puedes comprobarlo con go tool compile o simplemente mirando el código ensamblador:

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

Blogs

Book Recommendations