The split_at_mut
function (for splitting mutable slices at an index) requires unsafe code (according to the Rust book) to be implemented. But the book also says: "Borrowing different parts of a slice is fundamentally okay because the two slices aren’t overlapping".
My question: Why does the borrow checker not understand that borrowing different parts of a slice is fundamentally okay? (Is there a reason preventing the borrow checker from understanding this rule or is it just that it has not been implemented for some reason?)
Trying the suggested code with Rust 1.48 still results in the error shown in the book:
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = slice.len();
assert!(mid <= len);
(&mut slice[..mid], &mut slice[mid..])
}
fn main() {
let mut vector = vec![1, 2, 3, 4, 5, 6];
let (left, right) = split_at_mut(&mut vector, 3);
println!("{:?}, {:?}", left, right);
}
Gives error message:
error[E0499]: cannot borrow `*slice` as mutable more than once at a time
--> src/main.rs:6:30
|
1 | fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
| - let's call the lifetime of this reference `'1`
...
6 | (&mut slice[..mid], &mut slice[mid..])
| -------------------------^^^^^--------
| | | |
| | | second mutable borrow occurs here
| | first mutable borrow occurs here
| returning this value requires that `*slice` is borrowed for `'1`
Why does the borrow checker not understand that borrowing different parts of a slice is fundamentally okay?
Because it's impossible to do generally. Sure, in this case it's obvious that &slice[..mid]
and &slice[mid..]
are disjoint, but the complexity skyrockets once you escape trivial cases and it quickly becomes impossible.
These special trivial cases aren't implemented specially because:
split_at
and its mutable counterpartIndex
trait, which means that (&slice[..mid], &slice[mid..])
would be valid but (&vec[..mid], &vec[mid..])
wouldn't be, which is even more inconsistent. Of course this could be solved by making Vec
a language intrinsic, but then what about VecDeque
or user-defined data structures? It just leads to too much inconsistency that spirals into more inconsistency, which is something that Rust wants to avoid.Rust’s borrow checker can’t understand that you’re borrowing different parts of the slice; it only knows that you’re borrowing from the same slice twice. What if the two slices are overlapping? There's no way to grantee that their not. (If there is, it isn't implemented yet)
That's why unsafe exists: when you know for sure that there's no way that your code will produce an unexpected behavior despite the compiler not being able to grantee that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With