Does Rust prevent more logic bugs than statically-typed pure FP languages?

36 points verdagon

I've sometimes heard other Rust users express that it feels like "if it compiles, it works" more than other languages. I've felt this too, and I'm trying to figure out what causes this (more specifically than just "the borrow checker"), and where else these benefits might also be seen.

Some guesses about why, off the top of my head:

  • In most languages, we can find ourselves in situations where we assume that some existing data doesn't change. When we then break that assumption, we can cause bugs. However, when we have a borrow in Rust, it means nobody can accidentally change that data, so nobody can accidentally violate our assumptions.
  • In Rust, if we want to mutate an object, we need an &mut to it, which often means we first need a &mut reference to its parent. This forces us to consider the parent object (and all of its other children) in any mutation, and so reminds us to maintain invariants between them. We could call this a "top down" style of coding. Brian Will's video about OOP also talks about this more "top-down mutation" approach, which resonates with my Rust experience.
  • In Rust, we generally have to refactor more and think more about how we're doing things before we can successfully compile (because there are extra constraints at compile time compared to Java, specifically aliasability-xor-mutability), which happens to give us more time to think of possible bugs.
  • (any other reasons?)

So now, my main question: are these points also present in (statically typed) pure functional languages like Haskell? Respectively:

  • Pure FP languages also enforce that existing data doesn't change.
  • In pure FP languages, our data is still organized hierarchically, and to "modify" a piece of data, we have to recreate it, and its parent, and its parent, and so on, to get a new entire world. So, we have to "go through" its parent anyway.
  • We probably have to refactor more in pure FP languages too (because it also has extra constraints compared to Java, specifically that everything's immutable).

Would love to hear your thoughts! Are there any logic bugs that Rust prevents that e.g. Haskell does not?

(Note that I'm specifically talking about the quality of reducing logic problems, let's keep away from comparing the languages overall, or their other aspects like performance.)