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