rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.82k stars 12.5k forks source link

Better diagnostics when calling "ouroboros method" #70274

Open jonas-schievink opened 4 years ago

jonas-schievink commented 4 years ago

This code gives a somewhat hard to understand error message:

struct S<'a> {
    p: &'a (),
    unit: (),
}

impl<'a> S<'a> {
    fn ouroboros_me(&'a mut self) {
        self.p = &self.unit;
    }
}

fn main() {
    let unit = ();
    let mut s = S { p: &unit, unit: () };
    s.ouroboros_me();
    s.ouroboros_me();
}
error[E0499]: cannot borrow `s` as mutable more than once at a time
  --> src/main.rs:13:5
   |
12 |     s.ouroboros_me();
   |     - first mutable borrow occurs here
13 |     s.ouroboros_me();
   |     ^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

The error is correct, as ouroboros_me stores a self-reference in s, but it also happens when not doing that purely due to the method signature. That's also correct, but users frequently write such a method accidentally and are then left to wonder why the variable stays borrowed for seemingly no reason.

It would be good to give a more targeted explanation as to why the borrow stays active for longer than expected.

estebank commented 3 years ago

Another repro from #73150:

Repro:

error[E0499]: cannot borrow `x` as mutable more than once at a time
  --> src/main.rs:12:13
   |
10 |     let result = foo(&mut x);
   |                      ------ first mutable borrow occurs here
11 |     drop(result);
12 |     let _ = x.bar();
   |             ^
   |             |
   |             second mutable borrow occurs here
   |             first borrow later used here

This seems like a reasonable category of error to encounter (where the same lifetime is used in an fn signature causing non-obvious lifetime bonding), that I believe we should tackle it specifically. I believe it would ideally look closer to the following:

error[E0499]: cannot borrow `x` as mutable more than once at a time
  --> src/main.rs:12:13
   |
3  | fn foo<'a>(_: &'a mut S<'a>) {}
   |               ------------- this type requires that the borrow last as long as `S<'a>`, consider using different lifetimes
4  | 
5  | impl<'a> S<'a> {
6  |     fn bar(&mut self) {}
   |            --------- this requires a mutable borrow
...
10 |     let result = foo(&mut x);
   |                      ------ first mutable borrow occurs here
11 |     drop(result);
   |     ------------ `x` is no longer accessible after this call
12 |     let _ = x.bar();
   |             ^ --- this call mutably borrows `x` after it is no longer accessible
   |             |
   |             second mutable borrow occurs here