How Our Rust-to-Zig Rewrite Is Going
A longform write-up on rewriting the Roc language compiler from Rust to Zig has prompted a broader examination of trade-offs between safety, performance, and tooling in systems languages. Commenters contrast Rust’s strong static guarantees and mature ecosystem with Zig’s extremely fast incremental builds, fine-grained memory control, and pre‑1.0 instability, debating whether adding Rust-like borrow checking to Zig is even feasible. The thread also delves into how much “memory safety” compilers and runtimes can realistically provide, the role of garbage collection and schedulers in languages like Go, and when it’s justified to accept more unsafe code in exchange for performance or ergonomics.
Rust vs Zig for compiler implementation
- Many agree Zig’s incremental builds (e.g., ~35 ms rebuilds) are a major draw for compiler work; Rust is seen as slower but improving, with an official roadmap for faster builds.
- Some note that in the current Roc compiler, full builds can still be slower in Zig than Rust; others emphasize Zig’s architecture is optimized for future performance.
- Several argue that the choice of language matters less than algorithms and data structures, though others counter that low‑level memory/layout control can yield order‑of‑magnitude wins once algorithms are fixed.
Memory safety, unsafe, and borrow checking
- Strong debate over whether adding a Rust‑style borrow checker to Zig (via static analysis or IR tooling) is feasible:
- One side: “impossible” or highly unergonomic without lifetime‑aware language design, traits, encapsulation, and restricted expressiveness.
- Other side: cites tools, custom analyzers, and examples (Ada/SPARK, Oxide‑like projects) as evidence that extra safety can be “bolted on,” albeit at cost and with false positives.
- Discussion of fundamental limits: aliasing and temporal safety analysis become undecidable in general; Rust solves this by restricting expressiveness (affine/linear ownership).
- Zig’s ReleaseSafe mode and debug allocators provide bounds checks, leak detection, double‑free / some UaF detection using non‑reused addresses, but not full temporal safety.
- Many stress that “memory safe” in Rust is a specific, limited guarantee; safe Rust can still miscompile or be unsafe due to compiler bugs (e.g., theoretical CVEs).
What “safety” means and where compilers fit
- Long subthread distinguishing:
- Memory safety (objective, formalizable).
- Broader “safety” (context‑dependent: security, correctness, human safety).
- Some argue safety is a system property, not a language property; a safe compiler can still emit vulnerable binaries.
- Others counter that component‑level guarantees are still valuable: reducing unsafe surface makes debugging, sandboxing, and trust boundaries more tractable.
- Debate on whether compilers are “security‑sensitive”:
- One view: compilers are not hardened against malicious inputs (e.g., LLVM’s stated model); UB in compilers is “just another bug.”
- Opposing view: compilers are critical trust roots; memory exploits in them can inject code into all downstream binaries.
Go runtime and scheduling
- One commenter claims Go’s scheduler is “the most sophisticated in the world” and can exceed Rust in throughput by trading memory for concurrency.
- Multiple replies challenge this as exaggerated and inconsistent with experience and benchmarks, pointing to high‑performance systems that end up manually optimizing memory anyway.
- Others note competing runtimes (JVM, CLR, Erlang) and that scheduling theory is deep; claim is broadly treated as unsubstantiated.
Build times, caches, and tooling
- Rust’s slow builds and large artifact directories are a recurring complaint; some projects see minutes‑long incremental builds due to heavy generics and macro/codegen use (e.g., some GraphQL stacks).
- Suggestions:
- Use shared/global target directories and tools like sccache.
- Cargo team is redesigning artifact layout to enable garbage collection and better cache management.
- Zig team explains that for tiny edits, incremental rebuild cost is dominated by dependency graph traversal and change detection, not codegen, so performance should be similar across architectures.
Zig maturity, breaking changes, and “production‑readiness”
- Tension around Zig’s pre‑1.0 status:
- Advocates: it’s “production‑ready” in practice; the pre‑1.0 label mainly preserves freedom to do frequent breaking changes. Successful production users are cited.
- Critics: frequent core breaking changes (e.g., I/O APIs) make it not ready for most production teams; argue for proper semver or edition schemes instead of open‑ended breakage.
- Some prefer Zig’s approach (honest volatility) over conservative, accretion‑heavy languages; others insist on long‑term stability for serious deployments.
Language choice, GC, and performance
- Discussion of OCaml and other managed/FP languages for compilers: historically successful, very fast compilers exist; some think Roc’s “must be systems language” assumption is too strong.
- Counterpoint: modern CPUs and language features (e.g., sophisticated closure allocation) change constraints; fine‑grained control over layout and allocation can matter a lot.
- Broader GC debate:
- Some argue anti‑GC sentiment is exaggerated and most real‑world services could tolerate modern GCs.
- Others describe high‑throughput / low‑latency systems where µs‑scale stalls matter and GC pauses (even “low‑latency” ones) are unacceptable, so GC is out.
Roc language reactions and open questions
- Several commenters find Roc interesting but are unsure about its target niche and use cases (scripting, plugins vs server/client app language; competition vs WASM, Gleam, Elm).
- Some like Roc’s pattern‑matching and zero‑allocation string patterns but point out a subtle bug in a routing example and worry about tricky semantics with embedded slashes.
- Minor style feedback: separate type‑annotation lines feel awkward to some compared to F#‑style syntax.
Culture, rewrites, and AI/Bun side threads
- Some see a budding wave of “rewriting Rust to X” noise and caution against language tribalism; others emphasize “right tool for the job” and accept rewrites when domain fit changes.
- Brief comparison to another project’s Zig‑to‑Rust rewrite sparks jokes about “UNO reverse” but also raises questions about long‑term sustainability of pre‑1.0 ecosystems.
- One thread asks why a large AI company acquired a JavaScript runtime vendor rather than just porting; answers focus on risk of startup failure, strategic control, and acquihire motivations.