Go Enums Suck

Go’s treatment of enums—really just integer constants with the `iota` helper—is criticized as primitive and unsafe compared to richer enum or sum-type systems in languages like Rust, Ada, Java, or even Pascal from the 1970s. Commenters debate whether Go should add true, type-safe enums with exhaustiveness checking and safer serialization, or whether its minimalist, “just use ints and codegen/protobuf if needed” philosophy is an intentional and acceptable trade-off for simplicity and ease of use in typical web and backend work. The exchange also touches on broader questions of type safety, error handling, and how far a language should go in adding features versus keeping the core small and opinionated.

What “enums” even are (enums vs sum types)

  • Several comments distinguish classic enums (finite sets of named constants, usually with integer backing and natural iteration/ordering) from algebraic/sum types (variants that can hold structured data and require pattern matching).
  • Rust’s enum is repeatedly cited as a sum type, not a “true enum” in the traditional sense, though others argue this distinction is not very useful in practice.
  • Some see attempts to collapse enums and sum types into one concept as confusing and conceptually harmful; others focus on their shared “choice among alternatives” use case.

Go’s current “enum” story

  • Go lacks a dedicated enum keyword; idiom is type T int plus const (...) with iota.
  • Criticisms:
    • Not a closed set; any underlying int can be constructed, including invalid values.
    • No built‑in string representation, exhaustiveness checking in switch, or prevention of mixing unrelated “enum” types beyond the base type.
    • iota is seen by some as dangerous for serialized values, since reordering constants silently changes numeric encodings.
  • Defenses:
    • Many find iota concise and very convenient, especially for bit flags.
    • Some argue Go’s “enums” are real enums (named constants of a distinct type) and that extra guardrails aren’t worth added complexity.
    • Passing arbitrary ints is viewed by some as obviously a caller bug rather than a type-system failure.

Workarounds and tools

  • Common patterns: code generators (go generate, third‑party tools, custom DSLs) to add stringification, JSON/Protobuf integration, and validation.
  • Protobuf enums are noted as a robust option if gRPC/Protobuf is already used, including safe evolution and cross‑language support.

Broader language‑design debate

  • One side praises Go’s minimalism, fast builds, simple tooling, and “blue‑collar” suitability for REST/DevOps work.
  • Another side calls the language primitive and “boneheaded” in its resistance to established features (generics, proper enums, sum types, null safety, richer type constraints).
  • Comparisons highlight that many older or other languages (Pascal, Ada, Rust, MLs, C#, Java) have stronger enums or ADTs, with compile‑time exhaustiveness and better integration with type systems.

Error handling and optionality

  • Some argue Go’s explicit error returns are sane and treat errors as normal control flow.
  • Others prefer Result/Either/Option‑style sum types and see Go’s lack of such constructs (plus nilability everywhere) as a major design gap.