Live data from Hacker News

The borrowchecker is what I like the least about Rust

viralinstruction.com

1–10 of 459 posts

Re: The borrowchecker is what I like the least about Rust

#2
I'm far from a Rust pro, but I think the dismissal of alternatives like Polonius seems too shallow. Yes, it is still in the works, but there's nothing fundamentally wrong about the idea of a borrow checker.

This is true both in theory and in practice, as you can write any program with a borrow checker as you can without it.

TFA also dismisses all the advantages of the borrow checker and focuses on a narrow set of pain points of which every Rust developer is already aware. We still prefer those borrowing pain points over what we believe to be the much greater pain inflicted by other languages.

Re: The borrowchecker is what I like the least about Rust

#3
For the disjoint field issues raised, it’s not that the borrow checker can’t “reason across functions,” it’s that the field borrows are done through getter functions which themselves borrow the whole struct mutably. This could be avoided by making the fields public so they can be referenced directly, or if the fields needs to be passed to other functions, just pass the the field references rather than passing the whole struct.

There are open ideas for how to handle “view types” that express that you’re only borrowing specific fields of a struct, including Self, but they’re an ergonomic improvement, not a semantic power improvement.

Re: The borrowchecker is what I like the least about Rust

#5

For the disjoint field issues raised, it’s not that the borrow checker can’t “reason across functions,” it’s that the field borrows are done through getter functions which themselves borrow the whole struct mutably. This could be avoided by making the fields public so they can be referenced directly, or if the fields needs to be passed to other functions, just pass the the field references rather than passing the who…

> For the disjoint field issues raised, it’s not that the borrow checker can’t “reason across functions,” it’s that the field borrows are done through getter functions which themselves borrow the whole struct mutably

Right, and even more to the point, there's another important property of Rust at play here: a function's signature should be the only thing necessary to typecheck the program; changes in the body of a function should not cause a caller to fail. This is why you can't infer types in function signatures and a variety of other restrictions.

Re: The borrowchecker is what I like the least about Rust

#6
IMO, it is reasonable that in the example given:

  struct Point {
      x: f64,
      y: f64,
  }

  impl Point {
      fn x_mut(&mut self) -> &mut f64 {
          &mut self.x
      }

      fn y_mut(&mut self) -> &mut f64 {
          &mut self.y
      }
  }
the returned references are, for the purposes of aliasing rules, references to the entire struct rather than to pieces of it. `x` and `y` are implementation details of the struct and not part of its public API. Yes, this is occasionally annoying but I think the inverse (the borrow checker looking into the implementations of functions, rather than their signature, and reasoning about private API details) would be more confusing.

I also disagree with the author that his rejected code:

  fn main() {
      let mut point = Point { x: 1.0, y: 2.0 };
      let x_ref = point.x_mut();
      let y_ref = point.y_mut();
      *x_ref *= 2.0;
      *y_ref *= 2.0;
  }
"doesn't even violate the spirit of Rust's ownership rules."

I think the spirit of Rust's ownership rules is quite clear that when calling a function whose signature is

  fn f(param: &'a mut T1) -> &'a mut T2;

`param` is "locked" (i.e., no other references to it may exist) for the lifetime of the return value. This is clear once you start to think of Rust borrow-checking as compile-time reader-writer locks.

This is often necessary for correctness (because there are many scenarios where you need to be guaranteed exclusive access to an object beyond just wanting to satisfy the LLVM "noalias" rules) and is not just an implementation detail: the language would be fundamentally different if instead the borrow checker tried to loosen this requirement as much as it could while still respecting the aliasing rules at a per-field level.

Re: The borrowchecker is what I like the least about Rust

#7
post #5

For the disjoint field issues raised, it’s not that the borrow checker can’t “reason across functions,” it’s that the field borrows are done through getter functions which themselves borrow the whole struct mutably. This could be avoided by making the fields public so they can be referenced directly, or if the fields needs to be passed to other functions, just pass the the field references rather than passing the who…

> For the disjoint field issues raised, it’s not that the borrow checker can’t “reason across functions,” it’s that the field borrows are done through getter functions which themselves borrow the whole struct mutably Right, and even more to the point, there's another important property of Rust at play here: a function's signature should be the only thing necessary to typecheck the program; changes in the body of a fu…

See Rust's golden rule: https://steveklabnik.com/writing/rusts-golden-rule

Re: The borrowchecker is what I like the least about Rust

#8
This post pretty much completely ignores the advantages of the borrow checker. I'm not talking about memory safety, which is it's original purpose. I'm talking about the fact that code that follows Rust's tree-style ownership pattern and doesn't excessively circumvent the borrow checker is more likely to be correct.

I don't think that was ever the intent behind the borrow checker but it is definitely an outcome.

So yes, the borrow checker makes some code more awkward than it would be in GC languages, but the benefits are easily worth it and they stretch far beyond memory safety.

Re: The borrowchecker is what I like the least about Rust

#9

For the disjoint field issues raised, it’s not that the borrow checker can’t “reason across functions,” it’s that the field borrows are done through getter functions which themselves borrow the whole struct mutably. This could be avoided by making the fields public so they can be referenced directly, or if the fields needs to be passed to other functions, just pass the the field references rather than passing the who…

It's super easy to demonstrate your point with the first example the article gives as well; instead of separate methods, nothing prevents defining a method `fn x_y_mut(&mut self) -> (&mut f64, &mut 64)` to return both and use that in place of separate methods, and everything works! This obviously doesn't scale super well, but it's also not all that common to need to structure this way in the first place.

Re: The borrowchecker is what I like the least about Rust

#10
post #7
post #5

Earlier quoted context omitted.

> For the disjoint field issues raised, it’s not that the borrow checker can’t “reason across functions,” it’s that the field borrows are done through getter functions which themselves borrow the whole struct mutably Right, and even more to the point, there's another important property of Rust at play here: a function's signature should be the only thing necessary to typecheck the program; changes in the body of a fu…

See Rust's golden rule: https://steveklabnik.com/writing/rusts-golden-rule

This seems to be a golden rule of many languages? `return 3` in a function with a signature that says it's going to return a string is going to fail in a lot of places, especially once you exclude bolted-on-after-the-fact type hinting like what Python has.

It's easier to "abuse" in some languages with casts, and of course borrow checking is not common, but it also seems like just "typed function signatures 101".

Are there common exceptions to this out there, where you can call something that says it takes or returns one type but get back or send something entirely different?

Post reply on HN