Support
Quality
Security
License
Reuse
Coming Soon for all Libraries!
Currently covering the most popular Java, JavaScript and Python libraries. See a SAMPLE HERE.
kandi's functional review helps you automatically verify the functionalities of the libraries and avoid rework.
Empowering everyone to build reliable and efficient software.
Installing from Source
./x.py <subcommand> [flags]
QUESTION
Why is Rust NLL not working for multiple borrows in the same statement?
Asked 2022-Apr-12 at 00:43First, I tried something like this:
let mut vec = vec![0];
vec.rotate_right(vec.len());
It can't be compiled because:
error[E0502]: cannot borrow `vec` as immutable because it is also borrowed as mutable
I thought that the Rust borrow checker could be smarter than this, so I found something called NLL, and it should solve this problem.
I tried the sample:
let mut vec = vec![0];
vec.resize(vec.len(), 0);
It could work, but why is it not working with rotate_right
? Both of them take a &mut self
. What's going on?
ANSWER
Answered 2022-Apr-12 at 00:43It is definitely an interesting one.
They are similar - but not quite the same. resize()
is a member of Vec
. rotate_right()
, on the other hand, is a method of slices.
Vec<T>
derefs to [T]
, so most of the time this does not matter. But actually, while this call:
vec.resize(vec.len(), 0);
Desugars to something like:
<Vec<i32>>::resize(&mut vec, <Vec<i32>>::len(&vec), 0);
This call:
vec.rotate_right(vec.len());
Is more like:
<[i32]>::rotate_right(
<Vec<i32> as DerefMut>::deref_mut(&mut vec),
<Vec<i32>>::len(&vec),
);
But in what order?
This is the MIR for rotate_right()
(simplified a lot):
fn foo() -> () {
_4 = <Vec<i32> as DerefMut>::deref_mut(move _5);
_6 = Vec::<i32>::len(move _7);
_2 = core::slice::<impl [i32]>::rotate_right(move _3, move _6);
}
And this is the MIR for resize()
(again, simplified a lot):
fn foo() -> () {
_4 = Vec::<i32>::len(move _5);
_2 = Vec::<i32>::resize(move _3, move _4, const 0_i32);
}
In the resize()
example, we first call Vec::len()
with a reference to vec
. This returns usize
. Then we call Vec::resize()
, when we have no outstanding references to vec
, so mutably borrowing it is fine!
However, with rotate_right()
, first we call <Vec<i32> as DerefMut>::deref_mut(&mut vec)
. This returns &mut [i32]
, with its lifetime tied to vec
. That is, as long as this reference (mutable reference!) is alive, we are not allowed to use have any other reference to vec
. But then we try to borrow vec
in order to pass the (shared, but it doesn't matter) reference to Vec::len()
, while we still need to use the mutable reference from deref_mut()
later, in the call to <[i32]>::rotate_right()
! This is an error.
This is because Rust defines an evaluation order for operands:
Expressions taking multiple operands are evaluated left to right as written in the source code.
Because vec.resize()
is actually (&mut *vec).rotate_right()
, we first evaluate the dereference+reference, then the arguments:
let dereferenced_vec = &mut *vec;
let len = vec.len();
dereferencec_vec.rotate_right(len);
Which is obviously a violation of the borrow rules.
On the other hand, vec.resize(vec.len())
has no work to do on the callee (vec
), and so we first evaluate vec.len()
, and then the call itself.
Solving this is as easy as extracting the vec.len()
to a new line (new statement, to be precise), and the compiler also suggests that.
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
No vulnerabilities reported