The borrowchecker is what I like the least about Rust

Scope of the criticism

  • Many commenters feel the article overstates borrow‑checker flaws and uses contrived examples; others say those examples accurately capture real ergonomic pain on large, evolving codebases.
  • Key pain described: small changes to ownership or data layout can force large refactors, making mature Rust code feel rigid.

Disjoint borrows, encapsulation, and function signatures

  • The “disjoint fields” complaint (e.g., x_mut / y_mut on Point) is widely argued to be fundamental to Rust’s model, not a missing optimization:
    • Methods that take &mut self are treated as borrowing the whole struct; private fields are not reasoned about individually.
    • This preserves Rust’s “golden rule”: a function’s signature alone must suffice for typechecking; callers must not depend on function bodies.
  • Workarounds: explicit “split” APIs (e.g., fn xy_mut(&mut self) -> (&mut f64, &mut f64)), making fields public, view types / partial‑borrow designs, or future “view”/subset annotations.
  • Polonius is mentioned: it can fix some lifetime bugs (e.g. a get_default-style example) but won’t change these fundamental rules.

Borrow checker vs alternatives (GC, indices, Rc/unsafe)

  • Some argue: for non–systems domains (e.g. scientific computing), GC languages (Julia, Python, OCaml, Go, Java) give faster iteration with acceptable performance; Rust’s borrow checker is “too much” for their needs.
  • Others counter that:
    • Rust’s ownership model improves not just memory safety but general correctness and “local reasoning,” especially in large, long‑lived, concurrent systems.
    • Refactors in Rust tend to be safer: the compiler becomes a strong gate against subtle bugs.
  • Common escape hatches:
    • Integer indices into arenas / vectors (often with generational indices) for graphs and back‑references: still memory‑safe in Rust (bounds checks, panics instead of UB) but can reintroduce logical “dangling reference” bugs.
    • Rc/Arc + RefCell/Mutex to push checking to runtime; or unsafe for custom data structures.
    • Critics note this partially undercuts the “all safety, zero compromises” narrative.

Graphs, cyclic data, and back‑references

  • Many agree Rust is awkward for back‑references and cyclic structures:
    • Typical patterns: indices, Rc/Weak, interior mutability, or unsafe pointer‑based implementations.
    • Some suggest static analysis over RefCell::borrow scopes could, in theory, restore more compile‑time guarantees, but this likely requires interprocedural analysis and complex annotations.

Concurrency and “fearless concurrency”

  • One side: Rust’s concurrency story (e.g., Send, Sync) is inseparable from the borrow checker; giving ownership to another thread is only safe because aliasing is tightly controlled.
  • Another side: other languages (Pony, Swift) show that static concurrency safety doesn’t require a Rust‑style borrow checker, though Rust’s model and its concurrency rules “rhyme.”

Ergonomics, skill, and culture

  • Some see borrow‑checker struggles as mostly a “skill issue” that fades with experience and idiomatic design (functional style, tree‑like ownership).
  • Others insist the ergonomic cost is real even for experienced users, especially when ownership has to change late in a project.
  • Several point out that Rust’s culture—talks and libraries focused on correctness—is itself a major benefit; the borrow checker attracted that community.
  • A recurring view: the borrow checker is not what people most enjoy about Rust day‑to‑day (Cargo, pattern matching, ADTs, ecosystem rank higher), but it is what made Rust distinctive and successful.