Enums, short for “enumerations,” are a way to define a fixed list of related values. While Go doesn’t have a built-in enum type like some other programming languages (e.g., Java or C#), it provides a powerful mechanism using constants and the iota keyword.
What is iota?
iota is a predeclared identifier in Go, primarily used to simplify the creation of enumerated constants. It starts at 0 for the first constant in a const block and increments by 1 for each subsequent constant. This makes it an ideal tool for creating enums, as it eliminates the need to assign values to each constant manually.
Basic Example of Enums in Go
Here’s how you might define days of the week without iota:
const (
Sunday  = 0
Monday  = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday  = 5
Saturday = 6
)
With iota, this can be simplified:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
The iota keyword automatically assigns the values 0 through 6 to Sunday through Saturday, making the code cleaner and more maintainable.
Advantages of Using iota
You don’t need to assign values manually
You don’t need to assign values manually
You can define custom values based on iota
Advanced Examples
Skipping Values
Sometimes, you may skip specific values. You can achieve this using the blank identifier _:
const (
  Red = iota
  _   // Skip 1
  Blue
  Green
)

// Red = 0, Blue = 2, Green = 3
Starting from a Different Offset
You can set an initial offset for your enum by performing arithmetic with iota:
const (
  Bronze = iota + 1  // Start at 1
  Silver
  Gold
)

// Bronze = 1, Silver = 2, Gold = 3
Using iota for Bitmask Enums
iota is also useful for defining bitmasks:
const (
  Read    = 1 << iota // 1 << 0 = 1
  Write               // 1 << 1 = 2
  Execute             // 1 << 2 = 4
)

// Read = 1, Write = 2, Execute = 4
This is particularly handy when you’re working with permissions or flags.
Grouping Constants
You can reuse iota within separate const blocks:
const (
  Circle = iota
  Square
  Triangle
)

const (
  Low = iota + 1 // Start at 1 in this block
  Medium
  High
)

// Circle = 0, Square = 1, Triangle = 2
// Low = 1, Medium = 2, High = 3
Practical Usage
Enums are commonly used to represent a set of related values. Let’s look at a more practical example: a logging level enum.
package main

import "fmt"

const (
  Debug = iota
  Info
  Warning
  Error
  Critical
)

func Log(level int, message string) {
  switch level {
  case Debug:
      fmt.Println("[DEBUG]", message)
  case Info:
      fmt.Println("[INFO]", message)
  case Warning:
      fmt.Println("[WARNING]", message)
  case Error:
      fmt.Println("[ERROR]", message)
  case Critical:
      fmt.Println("[CRITICAL]", message)
  default:
      fmt.Println("[UNKNOWN]", message)
  }
}

func main() {
  Log(Info, "Application started.") // [INFO] Application started
  Log(Error, "An error occurred.") // [ERROR] An error occurred.
}
In this example, iota helps define log levels and a switch statement maps the levels to corresponding messages.
Tips for Using Enums in Go
Use Descriptive Names: Name your constants to improve code readability
Validate Inputs: Enums are typically integers, so validate inputs to ensure they fall within the expected range
Use String Representations: Use a String() method for better logging and debugging
func (level int) String() string {
  switch level {
  case Debug:
      return "Debug"
  case Info:
      return "Info"
  case Warning:
      return "Warning"
  case Error:
      return "Error"
  case Critical:
      return "Critical"
  default:
      return "Unknown"
  }
}