Zig; what I think after months of using it

Zig’s Error Model and “Global” Error Type

  • Confusion around “ErrorType is global”: clarified as a single, whole-program error set (within the link-time unit), not per-module.
  • Errors are structurally typed: two error{Foo} definitions in different libraries are equal; unlike Zig’s nominal structs/enums.
  • The compiler can track which error variants functions may return and whether callers handle all of them.
  • Lack of payloads on errors is a pain point: associated data must be stored elsewhere (e.g., unions or out-parameters), reintroducing boilerplate and weakening inference.

Memory Safety, UB, and Zig vs Rust

  • Long argument over the claim that Zig is “safer than unsafe Rust.”
  • One side: all Zig code is effectively unsafe; Rust’s unsafe is isolated and can be checked with tools like Miri, which catch more UB than Zig’s current checks.
  • Other side: writing correct unsafe Rust is harder because of subtle reference rules and noise; Zig’s simpler pointer model plus external checkers or sanitizers might be “safe enough.”
  • Skepticism that a static checker can make Zig as safe as Rust without breaking existing code; current checker PoCs are incomplete.

Variable Shadowing and Immutability

  • Heated debate: some see shadowing as a key feature, especially in Rust-style immutable code, to model “evolving” values while preventing reuse of old states.
  • Critics argue it hides bugs and hurts readability; cite bad experiences in Go (:= vs =). Some want it disabled via lints.
  • Defenders stress ergonomics: without shadowing, code accumulates many similar names and more opportunities to pick the wrong one; shadowing offers self-imposed guardrails.

Design Choices: Traits, Destructors, Strings, Generics

  • No traits/typeclasses: framed as a deliberate choice for explicitness; critics call Zig’s “duck-typed” generics hard to reason about and poorly documented.
  • No RAII-style destructors: defer/errdefer are scope-based, not per-object; this makes some patterns (e.g., container of heap-owned values) clumsier but also exposes allocation strategy clearly.
  • No built-in string type: everything is []u8. Pro: forces explicit thinking about encoding and buffers; con: Unicode correctness and “character” semantics are pushed onto users, despite std unicode helpers.

Arbitrary-Size Integers and Bitfields

  • Zig’s arbitrary-width integers are praised; Rust can emulate via crates but not at the language level.
  • Debate on addressability of sub-byte values: C/C++ bitfields lack pointers; Zig introduces richer pointer types (including bit-packed) at the cost of complexity.

Rust vs Zig Adoption and Ergonomics

  • Some claim Rust has effectively “won” as a C replacement; others point to Rust’s complexity, compile times, and maintenance/hiring costs.
  • Views on Zig range from “niche but promising, especially for C interop and games” to doubt it will rival Rust without interfaces/closures and stronger safety story.
  • Broader theme: Zig prioritizes simplicity and explicitness; Rust prioritizes stronger guarantees, even at ergonomic and conceptual cost.