An unordered list of things I miss in Go

Nullability, nil, and type safety

  • Strong push for explicit nullability types: compiler-enforced non-null refs (Rust Option, C# nullable refs, Kotlin-style) are seen as a major safety win; dereferencing nil should not compile.
  • Counterpoint: Go’s design favors simple types and explicit error handling over a richer type system. Many accept nil as a trade-off; some argue nullability adds complexity and isn’t aligned with Go’s philosophy.
  • Practical pain points: modeling DB columns, JSON “absent vs null vs zero value”, and unsafe a.b.c chains. Workarounds include pointer-as-optional, Null[T]/Optional wrappers, or sql.Null*, all with downsides.
  • Debate whether Go could ever retrofit nullability or sum types without breaking compatibility; some think nullability is easier than full unions, others doubt either will happen.

Map iteration order, randomness, and ordered maps

  • Go maps randomize both hash seeding and start offset on each iteration. This is justified as:
    • Hash-DoS mitigation.
    • Enforcing the spec’s “unordered” promise and smoking out code that accidentally depends on order.
  • Critics find per-iteration randomization surprising and harmful to reproducibility, debugging, and simple tasks like printing stable CLI help output.
  • Comparisons: Python dicts preserve insertion order by design; Rust HashMap has per-map seeding but deterministic iteration; Java offers LinkedHashMap and TreeMap.
  • Strong subthread on terminology: unordered vs ordered-by-insertion vs sorted-by-key; hash maps vs trees; linked vs index-based ordered maps.
  • Some want an ordered map (insertion-ordered hash) in Go’s stdlib; others say:
    • If you need consistent order, sort keys (slices.Sorted(maps.Keys(m))) or use a tree/other structure.
    • Ordered hash maps add overhead and ambiguous performance expectations.

Default/named arguments and functional options

  • Lack of default/named args is viewed by some as a feature: it forces API designers to think, and you can use wrapper functions.
  • Others miss them for readability, especially around multiple bools or rarely-changed parameters; they cite Python and C# as more ergonomic.
  • Functional-options pattern is a common Go workaround but criticized as:
    • Verbose for general functions.
    • Awkward when implemented via closures (no equality, hard for middleware/testing).
    • Better when using typed or interface-based options.

Error handling ergonomics

  • Many dislike repetitive if err != nil { return ... } and want a ?-style operator or automatic propagation.
  • Others embrace explicit error returns as core to Go, using helper must()/must1() with panic for scripts or debugging.
  • panic/recover are noted as Go’s de facto exceptions, but mixing them with error is seen as confusing in serious code.

Go’s philosophy vs feature wishlists

  • One camp views omissions (nullability, enums, sum types, ordered maps, default params) as necessary constraints that keep Go simple and maintainable.
  • Another sees Go as dated: they want richer type systems, sum types, better null handling, enums, and more ergonomic syntax; some look to Borgo, Rust, C#, or Swift as “Go but with modern features.”