I have a question about how the borrow checker works to figure out that borrows don't last long enough. I have some sample code here that just pushes a reference onto a vector.
15 | let value1 =
| ------ binding value1 declared here
...
20 | &value1
| ^^^^^^^ borrowed value does not live long enough
21 | );
22 | }
| - value1 dropped here while still borrowed
...
26 | reference_array[0]
| --------------- borrow later used here
Now, I know this is the correct error, but how on Earth does the borrow checker know that value1 and reference_array are intertwined? If I comment out the code that pushes the value, I get the same error.
That's because all of the information that the compiler is using is in the function signature. You called
push_value(
&mut reference_array,
&value1
);
and therefore, the type checker knows that reference_array must be a Vec<&'a u32> (for some lifetime 'a) and that &value1 must be (or coerce to) a &'a u32. Then the borrow checker looks at whether value1 will actually live long enough, or reference_array short enough, for 'a to mean something non-contradictory.
More abstractly: The part of your program that matters to this question is how the multiple uses of one lifetime variable 'a relate to each other. That's what makes the connection between different function arguments that the borrow checker then checks.
If you had not used the same lifetime variable twice, then push_value wouldn't care about how it was called, but the push() in its body wouldn't compile.
This video might be a good watch for you (and not really long), IIRC it also explains well how the type signatures of functions are essential to borrow checking, and it looks like the main example being discusses is almost identical to the code you’ve been looking at:
Looks at the uses of all values to figure out when lifetimes must be valid
Uses those to figure out how[1] and for how long borrows of variables[2] last[3]
Looks at the uses again and makes sure each use doesn't conflict with a borrow
For example it's ok to copy something which is shared-borrowed, but it's not ok to overwrite something that's exclusive-borrowed or drop something that's borrowed in any way.
The signature says that *value must be borrowed for 'a -- it must remain borrowed for however long the lifetime 'a is valid. Moreover, the type of the Vec must be exactly Vec<&'a u32>. This effectively means that 'a must remain valid wherever the Vec is used.
So the borrow of value1 begins on L2 and lasts for whatever the lifetime in the type of reference_array is; let's call it 'r. reference_array is used on L4, so 'r must remain valid from L2 through L4. That means value1 is still shared-borrowed on L3.
But value1 drops at L3. That's a use of value1 that's incompatible with being borrowed.