Enums in Go
Go has no enum keyword. Instead, use this 3-step recipe:
1. Define a named type → type Status int
2. Declare constants → const ( Active Status = iota ... )
3. Add String() method → func (s Status) String() string { ... }1. iota — The Auto Counter
iota starts at 0 and increases by 1 for each line in a const block.
const (
A = iota // 0 ← iota starts here
B // 1 ← iota auto-increments
C // 2
)
iotaresets to0at every newconstkeyword.
2. Building an Enum (Full Pattern)
// Step 1 — named type (prevents mixing with plain int)
type Direction int
// Step 2 — constants using iota
const (
North Direction = iota // 0
East // 1
South // 2
West // 3
)
// Step 3 — String() so fmt.Println shows "North", not "0"
func (d Direction) String() string {
switch d {
case North: return "North"
case East: return "East"
case South: return "South"
case West: return "West"
default: return fmt.Sprintf("Direction(%d)", d)
}
}
fmt.Println(North) // "North" ← readable!
fmt.Println(East) // "East"3. Skip Zero — Avoid Silent Bugs ⭐
type Status int
const (
_ Status = iota // skip 0
Active // 1
Inactive // 2
Banned // 3
)Why? Go zero-initializes every variable. Without skipping 0, a freshly declared var s Status silently equals Active — a bug that’s hard to spot.
var s Status // = 0
fmt.Println(s == Active) // ❌ true! That's wrong — s was never set4. Bit Flags with 1 << iota
Use when a value can hold multiple states at once (e.g. permissions).
type Permission int
const (
Read Permission = 1 << iota // 1 → 001
Write // 2 → 010
Execute // 4 → 100
)
perm := Read | Write // combine: 3 → 011
fmt.Println(perm & Write != 0) // true — has write
fmt.Println(perm & Execute != 0) // false — no execute| Constant | Value | Binary |
|---|---|---|
Read | 1 | 001 |
Write | 2 | 010 |
Execute | 4 | 100 |
Read|Write | 3 | 011 |
5. String Enums (for JSON / APIs)
type Environment string
const (
Production Environment = "production"
Staging Environment = "staging"
Development Environment = "development"
)
fmt.Println(Production) // "production" — no String() needed| Integer enum | String enum | |
|---|---|---|
| Auto-printable | ❌ needs String() | ✅ built-in |
| JSON output | number (1) | string ("production") |
| Bit flags | ✅ | ❌ |
6. iota Quick Reference
| Pattern | Example | Values |
|---|---|---|
| From 0 | iota | 0, 1, 2… |
| Skip zero | _ = iota then next | 1, 2, 3… |
| Bit flags | 1 << iota | 1, 2, 4, 8… |
| Offset | iota + 10 | 10, 11, 12… |
7. Common Mistakes ⚠️
| Mistake | Problem | Fix |
|---|---|---|
0 is a valid state | uninitialized var silently matches | skip 0 with _ = iota |
No String() method | logs show 0, 1, 2 | add String() string |
No default in switch | unknown values silently ignored | always add default |
Using plain int | no type safety | always define a named type |
8. Interview Cheat Sheet
Q: How do you define an enum in Go?
Named type +
constblock +iota. E.g.type Status intwithconst (Active Status = iota + 1 ...).
Q: What is iota?
An auto-incrementing counter (starts at
0) inside aconstblock. Resets at each newconst.
Q: Why skip the zero value?
Go zero-initializes all variables. If
0is a valid state, an unset variable silently looks valid — a hidden bug. Skipping makes uninitialized = invalid.
Q: How do you make an enum print nicely?
Implement
String() string— this satisfiesfmt.Stringer.fmt.Printlncalls it automatically.
Q: What is 1 << iota for?
Bit flags — each constant gets a unique power of 2, so multiple flags can be combined with
|and checked with&.