Reputation: 21486
I can't understand why Rust complains ("can't borrow as immutable because also borrowed as mutable") in this code (even considering that .len()
is a "constant/harmless" function):
fn main() {
let mut xs1: [i32; 5] = [ 10, 20, 30, 40, 50];
let (slice1, slice2) = xs1.split_at_mut( (xs1.len()/2) ); // <----- ERROR
println!( "{:?}", slice1 );
println!( "{:?}", slice2 );
} // ()
While the split version is fine:
let aux = xs1.len()/2;
let (slice1, slice2) = xs1.split_at_mut( aux );
What's the rationale here?
Maybe I'm wrong but I always regarded both expressions as "mathematically equivalent" so to say. Not only len()
should be a const
method but also the input param for split_at_mut()
should be const
(in C++ parlance).
Still holding this question eight years later?
Upvotes: 2
Views: 208
Reputation: 42492
Maybe I'm wrong but I always regarded both expressions as "mathematically equivalent" so to say.
They are, but this here is an issue with the borrow checker, it is not perfect, and it has limitations.
In fact pretty much all static type systems without holes have will reject valid programs: the choice is to either reject valid programs or accept invalid ones.
And in this case as Sven Marnach notes it's an artefact of the strict expression evaluation order: if you desugar the call to:
<[_]>::split_at_mut(&mut xs1, xs1.len()/2);
you get the same error.
In essence, Rust interprets
xs1.split_at_mut(xs1.len() / 2)
as
{
let p1 = &mut xs1;
{
let p2 = xs1.len();
{
<[_]>::split_at_mut(p1, p2);
}
}
}
Not only
len()
should be aconst
method
... which it is?
but also the input param for
split_at_mut()
should be const (in C++ parlance).
That idea has no relevance to Rust, split_at_mut
takes ownership of its (non-self) parameter.
Upvotes: 6
Reputation: 602355
This is mostly a side effect of the expression evaluation order. When evaluating the call xs1.split_at_mut((xs1.len()/2))
, the first step is to determine what method is actually being called, which involves determining the type of the receiver. Since split_at_mut
takes an &mut [i32]
, xs1
gets mutably borrowed at this point. The call to xs1.len()
happens later, but at that point we already have a mutable borrow, so the immutable borrow is not possible.
If you store the length in a variable first, the immutable borrow happens first, and it ends at the end of the expression, so it doesn't overlap with the mutable borrow anymore.
Upvotes: 5