Inmutable and Mutable borrows in a call to "const" method for a "const" parameter

Solution 1:

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 a const 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.

Solution 2:

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.