How to Create Enum-Like Types in Go
Go does not have a dedicated enum keyword like Java, C#, or TypeScript. Instead, enum-like values are usually modeled with a named type plus constants, often using iota to generate sequential values.
1. A Simple Enum-Like Constant Set with iota
iota is a counter that starts at zero within a const block and increments for each constant specification:
package main
import "fmt"
const (
Red = iota
Green
Blue
)
func main() {
fmt.Println("Red:", Red)
fmt.Println("Green:", Green)
fmt.Println("Blue:", Blue)
}Output:
Red: 0
Green: 1
Blue: 2This works, but the constants are still untyped integer constants and do not communicate much domain meaning by themselves.
2. Add a Named Type
A named type makes the intent clearer and gives methods a natural home:
type Color int
const (
Red Color = iota
Green
Blue
)Now functions can explicitly accept a Color instead of a general integer:
func paint(color Color) {
// ...
}3. Implement String() for Readable Output
Implement fmt.Stringer to display friendly names:
func (c Color) String() string {
switch c {
case Red:
return "Red"
case Green:
return "Green"
case Blue:
return "Blue"
default:
return "Unknown"
}
}Then:
fmt.Println(Green)prints:
GreenFor a larger set of constants, generation tools can help avoid maintaining repetitive String() switch statements manually.
4. Use Bit Flags for Combinable Values
When several options may be active at the same time—permissions are a common example—use distinct bits:
type Flags uint
const (
FlagA Flags = 1 << iota
FlagB
FlagC
FlagD
)
func main() {
flags := FlagA | FlagC
fmt.Println("Has FlagA:", flags&FlagA != 0)
fmt.Println("Has FlagB:", flags&FlagB != 0)
}Each constant occupies a separate bit, so values can be combined with bitwise OR and checked with bitwise AND.
Validate Unknown Values When Necessary
A named integer type does not prevent arbitrary values from being converted into it:
color := Color(99)If only specific values are valid at an API or storage boundary, validate them explicitly:
func (c Color) Valid() bool {
switch c {
case Red, Green, Blue:
return true
default:
return false
}
}Conclusion
Go typically models enums with named types and constants rather than a dedicated language feature. Use iota when sequential or bit-shifted constants make sense, implement String() for readable output, and add validation when external input must be restricted to known values.