Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot borrow as immutable because it is also borrowed as mutable in function arguments

What is going on here (playground)?

struct Number {     num: i32 }  impl Number {     fn set(&mut self, new_num: i32) {         self.num = new_num;     }     fn get(&self) -> i32 {         self.num     } }  fn main() {     let mut n = Number{ num: 0 };     n.set(n.get() + 1); } 

Gives this error:

error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable   --> <anon>:17:11    | 17 |     n.set(n.get() + 1);    |     -     ^          - mutable borrow ends here    |     |     |    |     |     immutable borrow occurs here    |     mutable borrow occurs here 

However if you simply change the code to this it works:

fn main() {     let mut n = Number{ num: 0 };     let tmp = n.get() + 1;     n.set(tmp); } 

To me those look exactly equivalent - I mean, I would expect the former to be transformed to the latter during compilation. Doesn't Rust evaluate all function parameters before evaluating the next-level-up function call?

like image 699
Timmmm Avatar asked Dec 16 '16 15:12

Timmmm


1 Answers

This line:

n.set(n.get() + 1); 

is desugared into

Number::set(&mut n, n.get() + 1); 

The error message might be a bit more clear now:

error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable   --> <anon>:18:25    | 18 |     Number::set(&mut n, n.get() + 1);    |                      -  ^          - mutable borrow ends here    |                      |  |    |                      |  immutable borrow occurs here    |                      mutable borrow occurs here 

As Rust evaluates arguments left to right, that code is equivalent to this:

let arg1 = &mut n; let arg2 = n.get() + 1; Number::set(arg1, arg2); 

Editor's note: This code example gives an intuitive sense of the underlying problem, but isn't completely accurate. The expanded code still fails even with non-lexical lifetimes, but the original code compiles. For the full description of the problem, review the comments in the original implementation of the borrow checker

It should now be obvious what is wrong. Swapping those first two lines fixes this, but Rust does not do that kind of control-flow analysis.

This was first created as bug #6268, now it is integrated into RFC 2094, non-lexical-lifetimes. If you use Rust 1.36 or newer, NLL is enabled automatically and your code will now compile without an error.

See also:

  • Why is a mutable borrow in an argument disallowed for a mutable method call?
like image 90
wimh Avatar answered Oct 02 '22 14:10

wimh