rust-lang / rust

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

E0502: Borrow checker fails when borrowing with opposite conditions #98014

Open Pegasust opened 2 years ago

Pegasust commented 2 years ago

Given the following code:

fn no_compile_main() {
    let mut _buf0 = 12;
    let mut _buf1 = 14;

    for i in 0..5 {
        let last = if i % 2 == 0 {&_buf0} else {&_buf1};
        let this = if i % 2 == 0 {&mut _buf1} else {&mut _buf0};
        *this += 1;
        println!("i: {}, last: {}, this: {}", i, last, this);
    }
}

fn main() {
    let mut _buf0 = 12;
    let mut _buf1 = 14;
    for i in 0..5 {
        let (last, this) =
            if i % 2 == 0 {
                (&_buf0, &mut _buf1)
            } else {
                (&_buf1, &mut _buf0)
            };
        *this += 1;
        println!("i: {}, last: {}, this: {}", i, last, this);
    }
}

The current output is:

error[[E0502]](https://doc.rust-lang.org/stable/error-index.html#E0502): cannot borrow `_buf1` as mutable because it is also borrowed as immutable
  --> src/main.rs:8:35
   |
7  |         let last = if i % 2 == 0 {&_buf0} else {&_buf1};
   |                                                 ------ immutable borrow occurs here
8  |         let this = if i % 2 == 0 {&mut _buf1} else {&mut _buf0};
   |                                   ^^^^^^^^^^ mutable borrow occurs here
9  |         *this += 1;
10 |         println!("i: {}, last: {}, this: {}", i, last, this);
   |                                                  ---- immutable borrow later used here

error[[E0502]](https://doc.rust-lang.org/stable/error-index.html#E0502): cannot borrow `_buf0` as mutable because it is also borrowed as immutable
  --> src/main.rs:8:53
   |
7  |         let last = if i % 2 == 0 {&_buf0} else {&_buf1};
   |                                   ------ immutable borrow occurs here
8  |         let this = if i % 2 == 0 {&mut _buf1} else {&mut _buf0};
   |                                                     ^^^^^^^^^^ mutable borrow occurs here
9  |         *this += 1;
10 |         println!("i: {}, last: {}, this: {}", i, last, this);
   |                                                  ---- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.

Ideally the output should look like:

...
If the mutable/immutable borrows are under the opposite condition, restructure the code using tuple destructuring:
let mut a = if cond {&mut _a} else {&mut _b};
let b = if cond {& _b} else {& a};

into
let (mut a, b) = if cond { (&mut _a, &_b) } else { (&mut _b, &a) };

The problem still exists in the beta and nightly versions. Illustrated code in Godbolt for different versions

mbartlett21 commented 2 years ago

Perhaps there should be a clippy lint for having the same condition multiple times.

kasparthommen commented 2 months ago

I just stubled over the same issue, albeit with two mutable borrows, which leads to a different error message.

In my view, the compiler should be able to prove that the logic in no_compile_main() is equivalent to the logic in main() and thus permit the code.