Why this code pass NLL check and compile?

i have been reading this blog
http://smallcultfollowing.com/babysteps/blog/2017/02/21/non-lexical-lifetimes-using-liveness-and-location/
and try to understand region liveness.
i don't understand why following code can compile. i mean according to the definition of liveness then there should be two live mutable borrows in line 12, one is i and another is z.

  1 fn main() {
  2 
  3     let mut i = 100;
  4     example2(&mut i);
  5 
  6 }
  7 
  8 fn example2(i: &mut i32) {
  9     let z = &mut *i;
 10     do_with_mut(z);
 11     do_with_mut(z);
 12     println!("{}", *z);
 13
 14     println!("{}", *i);
 15 
 16 }
 17 
 18 
 19 fn do_with_mut(i: &mut i32) {
 20     println!("value is {}", *i);
 21 }
 22 

z is a reborrow of i. As long as z is used, you can't use i, but once z is not used anymore, you can continue using i.

5 Likes

It deserves a more thorough writeup than I have a citation for, but here's a short introduction to reborrows (with some related notes in the surrounding pages).

3 Likes

it make sense from your point of view. what confuse me is that doesn't that violate the rust principal that
at any point there is only one mutable borrow or multiple immutable borrow of a object.

As all good "principles" do, this one, too, comes with some asterisks. In the context of re-borrows, the relevant elided detail is that (mutable) references can become somewhat "inactive"/"inaccessible" for the duration of them in turn being borrowed. [1]

We don't include the whole story of what the rules for borrowing in Rust is in such a principle, because the point of a statement like "at any point there is only one mutable borrow or multiple immutable borrow of a object" is absolutely not to empower the reader to write the next Rust compiler. Instead its point is to allow the Rust novice to start developing their first mental model of how borrowing works.


  1. The situation is analogous to how a normal variable owning some data becomes inaccessible while mutably borrowed. For example, something that the basic principle you quoted doesn't address either: assignment like "x = 42" does not create any borrows at all, yet an existing active borrow of x prevents such assignment, as access to x is limited by such a borrow. ↩ī¸Ž

4 Likes

That's an oversimplification or you are taking it too literally. The real rule is that there can't be aliasing mutable borrows, ie. two distinctly-derived mutable borrows that point to the same place. Reborrows are explicitly allowed and encoded in the language; tentative formal models (such as Stacked Borrows and Tree Borrows) have this property at their core.

3 Likes

consider following code which doesn't pass the compile

fn main() {
    
    let mut i = 100;
    example3(i);

}

fn example3(mut i:i32) {
    let ii=&mut i;
    let z = &mut i;
    do_with_mut(z);
    do_with_mut(z);
    println!("{}", *z);
    println!("{}", *ii);
}


fn do_with_mut(i: &mut i32) {
    println!("value is {}", *i);
}

i think the code pattern of function example3 is basically the same as example2. the only difference is the variable z is not a reborrow anymore.

my point is that there must be some case that the code pattern in example3 is totally wrong, so the compiler reject the code. and the code pattern in example2 must be correct in every possible case.

but i fail to come up with a bad case for the code pattern in example3.

No, it isn't.

That's a very important difference.

Change the type from i32 to anything managing heap-allocated memory, e.g. String or Box. Mutating such a type can involve a re-allocation, invalidating all other references. Example.

Forget the reborrows for a moment and consider field accesses:

struct MyStruct {
    my_field: i32,
}

fn takes_mut_ref(m: &mut MyStruct) {
    let field_ref = &mut m.my_field;
    *field_ref = 10;
    *m = MyStruct { my_field: 20 };
}

Here, both m and field_ref are references that point at my_field, so shouldn't this be disallowed? Well, there's also this similar example, which is not allowed:

struct MyStruct {
    my_field: i32,
}

fn takes_mut_ref(m: &mut MyStruct) {
    let field_ref = &mut m.my_field;
    *m = MyStruct { my_field: 20 };
    *field_ref = 10;
}
error[E0506]: cannot assign to `*m` because it is borrowed
 --> src/lib.rs:8:5
  |
7 |     let field_ref = &mut m.my_field;
  |                     --------------- `*m` is borrowed here
8 |     *m = MyStruct { my_field: 20 };
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*m` is assigned to here but it was already borrowed
9 |     *field_ref = 10;
  |     --------------- borrow later used here

The explanation is as follows:

You can have more than one mutable reference to something. However, at any one time, only one of them can be usable.

Let's annotated the first example:

fn takes_mut_ref(m: &mut MyStruct) {
    // Initially, m is usable.
    // By creating field_ref, we make field_ref usable,
    // but m becomes unusable.
    let field_ref = &mut m.my_field;
    // Since field_ref is usable, we can write to it.
    *field_ref = 10;
    // After the last use, field_ref stops being usable, and
    // m becomes usable.
    // Since m is now usable again, we can write:
    *m = MyStruct { my_field: 20 };
}

However, in the second example, we cannot do this. To access m, we must make my_field unusable, but once we make field_ref unusable, we can't make it usable again. This prevents us from using it again after the use of m.

As for reborrows, they behave in the same.The reborrow is a sub-reference and the original reference becomes unusable until the last use of the sub-reference. So even though we have two mutable references, only one of them is usable at the time.

9 Likes

If you're experimenting with ownership and borrowing, I strongly suggest using String instead of primitive integers.

(Anything that's Copy -- like i32 is -- opts-out of some of the usual restrictions, and I've seen that confuse people before.)

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.