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?
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
- Q548Explain the functional options pattern. Compare it with a config struct and a builder. How do you validate options and return errors?
- Q549How do you implement a thread-safe singleton or lazy initialization in Go? Compare sync.Once, sync.OnceValue and package-level init.
- Q551How do variadic functions work? What is the aliasing trap when passing s... to a function that modifies or appends to its argument?
- Q552Explain closures in Go: what they capture, when captured variables escape to the heap, and how to build a generator or middleware with them.
- Q553Why does Go have no inheritance, function overloading, default arguments, ternary operator or exceptions? How do idiomatic Go programs work around each?
- Q554Can you compare functions in Go? Why is a func only comparable to nil, and how does that affect using funcs as map keys?