just want say a little bit about how you should think of the borrow checker, in the following snippet:
struct Bar(usize);
struct Foo<'a>(&'a mut Bar);
fn main() {
let mut bar = Bar(42);
let foo = Foo(&mut bar); // (1) mut borrow of `bar` starts
foo.0.0 += 1; // (2) the borrow is **used**
let baz = bar; // (3) `bar` moved, invalidates borrowed reference
} // (4) end of lexical scope
this code compiles, as Foo
is "trivially droppable", this does NOT mean Foo
has some magical compiler synthesized "no-op" drop glue (or "destructor"), this literally means there's NO drop glue for this type.
the reason this code compiles is not because the compiler reordered the code and moved the hypothetical "no-op" from (4)
to somewhere before (3)
: there's nothing to move or reorder.
the borrow checker is NOT implemented in this way: keeping track of the beginning and "end" (read: drop) of borrows for a borrowed value and calculate the disjunction of their life spans, and when the value is moved or dropped, it checks whether or not it is outside of span of the all the borrows.
instead, it is implemented the other way around: keeping track of the status of the references, borrows are created as valid. when a value is dropped or moved, it invalidates all the references. when a reference is used, it checks whether the reference is still valid.
in the example code above, when Foo
is "trivially droppable" (i.e. doesn't implement Drop
), there's literally no drop glue code for it. so the program is accepted by the borrow check according to the rules.
but if you implements Drop
for Foo
, compiler will insert drop glue code at (4), which counts as a use of the inner mut reference to bar
, (even if the Drop::drop()
is a "no-op" function), but the reference is invalidated at (3), so the borrow checker reject the code and reports a violation.
on the surface, one may think a reference can "end early" before reaching the end of the lexical scope, and may have the impression that the compiler can reorder code and move the (hypothetical) drop or "destructor" of "reference types" early. but in reality there's no "drop" at all for the reference type & T
and &mut T
, their lifetime doesn't ends because they are "dropped" going out of scope, it's the last use of references that affects their perceived "lifetime".