Go

How do you model enums in Go with iota? How do you add String() via go generate stringer, validate values, and handle the zero value?

Question 550MediumGo 1.22 to 1.25

Define a named integer type and a const block that uses iota. Go does not have closed enums: Status(42) compiles, and untyped constants convert implicitly, so validation has to be explicit.

//go:generate go run golang.org/x/tools/cmd/stringer -type=Status -trimprefix=Status
type Status int

const (
	StatusUnknown   Status = iota // 0: zero value means "not set"
	StatusActive                  // 1
	StatusSuspended               // 2
	statusEnd                     // sentinel for range checks, unexported
)

func (s Status) Valid() bool { return s > StatusUnknown && s < statusEnd }

func (s Status) MarshalText() ([]byte, error) {
	if !s.Valid() {
		return nil, fmt.Errorf("invalid status %d", int(s))
	}
	return []byte(s.String()), nil
}

Running go generate ./... writes status_string.go. The generated file includes a compile-time guard that fails to build if the constant values change without regenerating, and it prints Status(42) for unknown values. Zero value: make 0 mean "Unknown/Invalid" so that an uninitialized struct field or a missing JSON field is detectable. Otherwise a zero-valued struct silently gets your first real state. Use iota + 1 or _ = iota to skip it. Gotchas: never persist iota values to a DB or the wire and then reorder constants. Store strings, or give the numbers explicit values. For bit flags use 1 << iota. Add UnmarshalText that rejects unknown names so the check also runs at system boundaries.

More on Go Idioms, Design Patterns & Language Design

All 16 Go Idioms, Design Patterns & Language Design questions