Why does the reborrow escape the borrow check?

fn main(){
   let mut v = 0;
   let mrf = & mut v;
   let rf = &*mrf;
   println!("{}",mrf);
}

This example is ok. However, we say the mutable borrow excludes the immutable borrow. Desugar the lifetime of the above example will get the following diagram:

'v:{
   let mut v = 0;
   'a:{
     let mrf = & mut v;    // -------------------------------  the mutable borrow mrf begins
     'b:{
       let rf = &*mrf;     // --------------------------- the immutable borrow begins
     }                          // ---------------------------  rf ends
     println!("{}",mrf);   // ------------------------------- mrf ends
   }
 }

It can be seen that the lifetime of the immutable reference overlaps with the lifetime of the mutable borrow. However, the compiler can pass through this example. What's the reason here?

Immutable borrow is created by reborrowing the mutable one, so, strictly speaking, they don't coexist - the mutable borrow is temporarily rendered unusable, as long as its reborrow exists.

From a certain perspective, Is this considered to be the magic created by the compiler?

Not really. If that's magic, then this is too:

fn eval_capacity(v: &mut Vec<i32>) {
    let _ = v.capacity();
}

since Vec::capacity requires creation of &Vec<T>.

So, why does it have the privilege that it behaves differently from the diagram expects?

The original borrow, from which the re-borrow was created, is unusable as long as the re-borrow is in use. So the diagram you created isn't a correct illustration of the situation. You should temporarily end the validity of the original borrow when the re-borrow starts, and restart its validity once the re-borrow ends.

In general, lifetimes and regions are not expressible using scopes only; that's too simplistic a mental model. You need to take control flow into account, too.

Could you please illustrate the diagram of the actual situation? I think that may be helpful to understand what you guys said.

fn main(){
    let mut v = 0;
    let mrf = &mut v;    // mutable borrow starts here

                         // re-borrow starts here;
    {                    // mutable borrow temporarily unavailable
        let rf = &*mrf;
    }                    // re-borrow ends here;
                         // original borrow available again

    println!("{}", mrf);
}
2 Likes

Perhaps these diagrams linked before will click more now that you're considering how reborrows work.

1 Like

Anyway, how do you interpret this example?

fn main(){
  let mut v = 0;
  let mrf = & mut v;
  let rf = &*mrf;
  println!("{}",mrf);  // #1
  println!("{}",rf);  // #2
}

You said:

The original borrow, from which the re-borrow was created, is unusable as long as the re-borrow is in use.

The reborrow rf is used at #2, according to your interpretation, the original borrow should keep being unusable until #2. The actual is, it can still be used at #1.

The answer to that is that "the &mut borrow is unusable" is a slight simplification. More accurate is "the &mut borrow acts like a & borrow."

So a fuller picture could be

// `place` begins
let mut place = 0;

// '1 is derived from `place`
// `place` mayn't be used
// '1 may be used by-mut
let uniq: &'1 mut i32 = &mut place;

// '2 is derived from '1
// '2 mayn't be used by-mut
// '1 mayn't be used by-mut
let shr: &'2 i32 = &*uniq;

// '1 is used by-ref
dbg!(uniq);

// '2 is used by-ref
dbg!(shr);

// '2 ends
// '1 may again be used by-mut
// '1 ends
// `place` may again be used
// `place` ends
1 Like