Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mysterious borrow scope extension

Tags:

rust

Why does the compiler reject this code:

struct S<'a> {
    i: i32,
    r: &'a i32,
}

fn main() {
    let mut s = S{i: 0, r: &0};
    {
        let m1 = &mut s;
        m1.r = &m1.i;
    }
    let m2 = &mut s;
}

The error is: "cannot borrow s as mutable more than once at a time" (first borrow: m1, second borrow: m2).

Why is the first borrow of s still alive after m1 goes out of scope?

I read about borrow scope extension beyond the scope of the original borrower. However, this always seemed to involve another borrower outside the scope of the original borrower that "took over" the original borrow, e.g. this code fails with the exact same error, which is clear to me:

fn main() {
    let mut s = 0;
    let r: &mut i32;
    {
        let m1 = &mut s;
        r = m1;
    }
    let m2 = &mut s;
}

In the first example, if I replace m1.r = &m1.i; with m1.r = &dummy; (dummy defined as some &i32) or with let dummy = &m1.i;, the code compiles. The error occurs only if I store a reference to a field in another field of the borrowed struct. I don't see why this should extend the borrow beyond its scope.

My best guess as to what is wrong with the code is:

  • s.r's original lifetime is the whole of main,

  • when I assign a reference to m1.r it has to be that original lifetime, but &m1.i is only valid for as long as m1 lives.

But I might be wrong (the error message would be misleading then).

like image 201
dacker Avatar asked Aug 22 '15 03:08

dacker


1 Answers

First note that

let mut s = S{i: 0, r: &0};
{
    s.r = &s.i;
}
let m2 = &mut s;

Gives

cannot borrow `s` as mutable because `s.i` is also borrowed as immutable

Hopefully this should be clear - if a struct self-borrows then it is borrowed. This points out why any self-borrowing structure is basically useless - it cannot be moved (invalidating its own pointer) nor can and mutable reference be taken to it.


Next one needs to understand that immutable references from mutable references count as borrows into the mutable reference, so extend it. For example

let mut v = ();
let r1 = &(&mut v);
let r2 = &v;

gives

cannot borrow `v` as immutable because it is also borrowed as mutable

It's not clear if this is legally able to be a new borrow into the original structure, but it as-yet does not act as such.

like image 176
Veedrac Avatar answered Oct 18 '22 15:10

Veedrac