Go by Example

Enums

Go has no built-in enum type - idiomatic enums use typed integer constants with iota to generate sequential values.

Go does not have a first-class enum construct. The idiomatic approach is a typed integer constant group using iota, which increments automatically within a const block. This gives you type safety and a compact declaration, while keeping the language simple.

Declare a named integer type and a block of constants using iota. The first constant in the block gets iota = 0, and each subsequent constant increments it by one.

package main
 
import "fmt"
 
type Direction int
 
const (
    North Direction = iota
    East
    South
    West
)
 
func main() {
    d := North
    fmt.Println(d)        // 0
    fmt.Println(d == North) // true
    fmt.Println(East)     // 1
    fmt.Println(West)     // 3
}

Implement the fmt.Stringer interface so your enum prints a readable name instead of an integer. This also makes log output and test failures much easier to read.

package main
 
import "fmt"
 
type Weekday int
 
const (
    Monday Weekday = iota + 1
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    Sunday
)
 
func (w Weekday) String() string {
    names := [...]string{"Monday", "Tuesday", "Wednesday",
        "Thursday", "Friday", "Saturday", "Sunday"}
    if w < Monday || w > Sunday {
        return fmt.Sprintf("Weekday(%d)", int(w))
    }
    return names[w-1]
}
 
func main() {
    fmt.Println(Wednesday) // Wednesday
    fmt.Println(Weekday(9)) // Weekday(9)
}

Use bitwise expressions with iota for flag-style enums where values can be combined with the | operator. Each constant is a power of two, so flags can be OR'd and AND'd together.

package main
 
import "fmt"
 
type Permission int
 
const (
    Read    Permission = 1 << iota // 1
    Write                          // 2
    Execute                        // 4
)
 
func (p Permission) String() string {
    result := ""
    if p&Read != 0 {
        result += "r"
    }
    if p&Write != 0 {
        result += "w"
    }
    if p&Execute != 0 {
        result += "x"
    }
    return result
}
 
func main() {
    p := Read | Write
    fmt.Println(p)           // rw
    fmt.Println(p&Execute != 0) // false - execute not set
}

In production

Go has no built-in exhaustiveness check for enum values - a switch statement that handles only some constants compiles without warning. Add a default case that panics or logs an error so you discover unhandled values when a new constant is added, rather than in a production incident. The stringer tool (go generate with //go:generate stringer -type=MyType) automates the String() method and keeps it in sync with the constants automatically - far safer than a hand-written string array that silently goes stale when constants are added or reordered.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free