GGistDev

Enums in Go

Go doesn’t have built-in enums, but you get enum-like behavior with typed constants and iota.

Core ideas

  • Use a custom type plus a const block with iota
  • Implement String() for readable printing
  • Validate inputs via switch or helper functions
  • Optionally implement encoding.TextMarshaler / TextUnmarshaler for JSON/text I/O

Basic enum-like pattern

package main
import "fmt"

type Status int

const (
    StatusPending Status = iota
    StatusRunning
    StatusCompleted
    StatusFailed
)

func (s Status) String() string {
    switch s {
    case StatusPending:
        return "Pending"
    case StatusRunning:
        return "Running"
    case StatusCompleted:
        return "Completed"
    case StatusFailed:
        return "Failed"
    default:
        return "Unknown"
    }
}

func main() {
    var s Status = StatusRunning
    fmt.Println(s, s.String()) // 1 Running
}

Starting from 1 or custom offsets

package main

// Roles start at 1
 type Role int
 const (
     RoleUser Role = iota + 1 // 1
     RoleModerator            // 2
     RoleAdmin                // 3
 )

Bit flags (advanced)

package main

// Use bit shifts for combinable flags
 type Perm uint8
const (
     PermRead Perm = 1 << iota // 0001
     PermWrite                 // 0010
     PermExec                  // 0100
 )

 func Has(p, flag Perm) bool { return p&flag != 0 }

Tips:

  • Keep flags small (uint8/uint16) unless you need many bits
  • Provide combine helpers and masks for clarity

Validating values

package main
import "fmt"

func IsValidStatus(s Status) bool {
    switch s {
    case StatusPending, StatusRunning, StatusCompleted, StatusFailed:
        return true
    default:
        return false
    }
}

func main() {
    fmt.Println(IsValidStatus(Status(99))) // false
}

Marshaling to/from text/JSON

func (s Status) MarshalText() ([]byte, error) { return []byte(s.String()), nil }
func (s *Status) UnmarshalText(b []byte) error {
    switch string(b) {
    case "Pending": *s = StatusPending
    case "Running": *s = StatusRunning
    case "Completed": *s = StatusCompleted
    case "Failed": *s = StatusFailed
    default: return fmt.Errorf("invalid status: %q", b)
    }
    return nil
}

Best practices

  • Prefer typed constants over untyped to prevent mixing domains (e.g., Status vs Priority)
  • Group constants in a block; keep order stable to avoid shifting values
  • Add String() methods for debugging, logs, and UX
  • Use bit flags only when you need combinable options; otherwise simple enums are clearer
  • Consider stringer tool (golang.org/x/tools/cmd/stringer) to generate String() and parsing code

Summary

  • Enums in Go = typed constants + iota
  • Provide String() and validation helpers for robustness