Zig, Rust, and Other Languages

Zig’s allocation model and “no hidden allocations”

  • Several comments dispute the article’s strong claim that Zig avoids hidden allocations.
  • Standard C functions mostly do not allocate; strdup is a notable exception.
  • In Zig, most stdlib APIs follow the “pass an allocator if this might allocate” convention, but not all:
    • std.Thread.spawn allocates (stack + heap for argument payload) without an explicit allocator.
    • std.ArrayList stores an allocator internally, so method calls can allocate even when no allocator is passed.
    • ArrayListUnmanaged exists to keep allocators at a higher level and make allocations explicit.
  • Some argue this is “just a convention” rather than a strict guarantee; others say that’s still valuable.

Memory management trade‑offs across languages

  • Nim is highlighted as a systems language with configurable memory management; default ARC is seen as more ergonomic than threading allocators through Zig code.
  • Counterpoint: one can use a global allocator or attach allocators to objects in Zig to avoid excessive plumbing.
  • Odin, C with good APIs and arenas, and others are cited to argue that API design matters more than language per se.

Standard library size vs ecosystem fragmentation

  • One camp prefers compact stdlibs to avoid C++‑style bloat, enable faster evolution, and reduce long‑term maintenance of obsolete APIs.
  • Another camp argues small stdlibs force reliance on many third‑party packages, leading to fragmentation, multiple competing libraries, and dependency hell.
  • Examples:
    • Python: large stdlib is generally seen as positive, though some modules are outdated; popular third‑party libs (e.g., HTTP clients) still win on ergonomics.
    • Go: praised stdlib but specific parts (logging, flags, earlier net.IP) are criticized; newer abstractions like netip improve things.
    • Rust: absence of compression in stdlib is defended as keeping evolving domains in crates; others point to good compressors in other ecosystems’ stdlibs as counter‑evidence.
  • Suggested compromise: small core stdlib + officially maintained “blessed” packages, or later adoption of de‑facto standards.

Package managers and dependency concerns

  • Zig’s package story:
    • Has package management via build.zig.zon and git references; upcoming 0.12 improvements are anticipated.
    • No central package repository by design (similar to Go); some see this as safer, others as a bad decision.
  • General concerns:
    • Multiple competing C/C++ package managers (vcpkg, conan, etc.) don’t interoperate well.
    • Rust’s cargo:
      • Avoids per‑project duplication of sources; dead code elimination mitigates binary bloat.
      • Problems cited: large caches and stale incremental artifacts; upcoming automatic cache GC on nightly is welcomed.
    • Some suggest using Nix instead of language‑specific managers.

Strings, Unicode, and type‑system guarantees

  • Zig:
    • Uses []const u8 for strings; by convention these are UTF‑8.
    • Functions often assume valid UTF‑8 without runtime checks; responsibility lies with the producer.
    • Critics liken this to “just don’t write bugs” and argue for encoding invariants in types.
  • Rust:
    • Distinguishes String (owned UTF‑8) and &str (borrowed UTF‑8), with runtime UTF‑8 validation in safe code.
    • Unsafe constructors allow skipping checks only when the programmer promises validity.
    • Separate types like OsString avoid forcing UTF‑8 where it’s not guaranteed.
    • This model is praised for safety and flexibility but criticized for complexity and learning overhead.
  • Go and others:
    • Go string is “just bytes”; can hold arbitrary data, not guaranteed UTF‑8.
  • Unicode support:
    • Zig’s std.unicode covers validation and codepoint iteration; more advanced support (graphemes, categories) is in external libraries.
    • Similar gaps exist in Rust and Go; full Unicode handling (e.g., grapheme iteration, locale‑aware sorting) is complex and often pushed to libraries.
  • Some argue that because “string” requirements are so varied (mutability, slicing, Unicode semantics, performance), it’s natural that ecosystems grow multiple string abstractions despite a built‑in type.