Reputation: 2791
Vec<T>
has two methods:
fn push(&mut self, value: T)
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])
They both take a mutable reference to the vector. But the scope of the borrow seems to be different, e.g:
fn works() {
let mut nums: Vec<i64> = vec![1,2,3,4];
nums.push(5);
println!("{}", nums.len());
}
fn doesnt_work() {
let mut nums: Vec<i64> = vec![1,2,3,4];
let (l,r) = nums.split_at_mut(2);
println!("{}", nums.len());
}
fn also_works() {
let mut nums: Vec<i64> = vec![1,2,3,4];
let _ = nums.split_at_mut(2);
println!("{}", nums.len());
}
The doesnt_work
function doesn't compile, saying there is already a mutable borrow on nums
and that it ends and the end of the function. The problem goes away if I ignore the values returned from split_at_mut
.
Upvotes: 2
Views: 954
Reputation: 2791
Let me answer my own question, since what I was really missing were lifetimes. This code compiles:
fn maybe_use<'a, 'b>(v1: &'a mut Vec<i64>, v2: &'b mut Vec<i64>) -> &'a mut Vec<i64> {
v1
}
fn main() {
let mut nums1: Vec<i64> = vec![1,2,3,4];
let mut nums2: Vec<i64> = vec![1,2,3,4];
let ret = maybe_use(&mut nums1, &mut nums2);
println!("{}", nums2.len());
}
Because the return type of maybe_use makes it clear the reference comes from the first argument. If we change v2
to use 'a
lifetime, main
stops compiling because both vectors passed to maybe_use
are considered borrowed. If we omit the lifetime altogether, compiler emits this error:
this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from
v1
orv2
So what surprised me initially (how does the compiler know split_at_mut
returns pointers to the vector?) boils down to the references having the same lifetime.
Upvotes: 3
Reputation: 949
The borrowing of nums
in doesnt_work
will last as long as the variables l
and r
exist because the values in the vector (and the vector itself) have literally been borrowed and are now accessible only through l
and r
.
You can see this effect by putting the let
for l
and r
in a scope which ends so the borrow also ends. For example this code works fine but if you try to move the println!
inside the scope (inside the curly brackets) then it will fail:
fn works() {
let mut nums = vec![1,2,3,4];
{
let (l, r) = nums.split_at_mut(2);
//println!("{}", nums.len()); //println! will fail here
}
println!("{}", nums.len());
}
In your also_works
example you don't do anything with the result so the borrow is lost immediately. Basically the compiler can see that there is no way you can access the vector through the result of the method so you're free to access them through the original vector.
Upvotes: 8