I'm prettye sure that this question has come up at some point, but sadly I haven't been able to find an explanation.
I'm wondering what the error "cannot borrow as mutable while also borrowed as immutable" means in the following case:
let mut v: Vec<usize> = vec![1,2,3,4,5];
v[v[1]] = 999;
After some digging I found that indexing is implemented via the traits Index
and IndexMut
and that v[1]
is syntactiv sugar for *v.index(1)
. Equipped with this knowledge I tried to run the following code:
use std::ops::{Index, IndexMut};
\\ -- snip --
let mut v: Vec<usize> = vec![1,2,3,4,5];
*v.index_mut(*v.index(1)) = 999;
To my surprise this works flawlessly! Can someone tell me why the first snippet doesn't work, but the second one does? The way I understand the documentation they should be equivalent, but this obviously isn't the case.
Thanks for helping a Rust newbie out! : )
The desugared version is slightly different than what you have. The line
actually desugars to
This results in the same error message, but the annotations give a hint as to what's happening:
The important difference to your desugared version is the evaluation order. The arguments of a function call are evaluated left to right in the order listed, before actually making the function call. In this case this means that first
&mut v
is evaluated, mutably borrowingv
. Next,Index::index(&v, 1)
should be evaluated, but this is not possible –v
is already mutably borrowed. finally, the compiler shows that the mutable reference is still needed for the function call toindex_mut()
, so the mutable reference is still alive when the shared reference is attempted.The version that actually compiles has a slightly different evaluation order.
First, the function arguments to the method calls are evaluated left to right, i.e.
*v.index(1)
is evaluated first. This results in ausize
, and the temporary shared borrow ofv
can be released again. Then, the receiver ofindex_mut()
is evaluated, i.e.v
is mutably borrowed. This works fine, since the shared borrow has already been finalised, and the whole expression passes the borrow checker.Note that the version that compiles only does so since the introduction of "non-lexical lifetimes". In earlier versions of Rust, the shared borrow would live until the end of the expression and result in a similar error.
The cleanest solution in my opinion is to use a temporary variable: