Apps Artificial Intelligence CSS DevOps Go JavaScript Laravel Linux MongoDB MySQL PHP Python Rust Svelte Vue

How to Create Enum-Like Types in Go

1 min read .
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: 2

This 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:

Green

For 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.

Related Posts

chevron-up