Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't a mutable borrow of self change to immutable?

This code fails the dreaded borrow checker (playground):

struct Data {
    a: i32,
    b: i32,
    c: i32,
}

impl Data {
    fn reference_to_a(&mut self) -> &i32 {
        self.c = 1;
        &self.a
    }
    fn get_b(&self) -> i32 {
        self.b
    }
}

fn main() {
    let mut dat = Data{ a: 1, b: 2, c: 3 };
    let aref = dat.reference_to_a();
    println!("{}", dat.get_b());
}

Since non-lexical lifetimes were implemented, this is required to trigger the error:

fn main() {
    let mut dat = Data { a: 1, b: 2, c: 3 };
    let aref = dat.reference_to_a();
    let b = dat.get_b();
    println!("{:?}, {}", aref, b);
}

Error:

error[E0502]: cannot borrow `dat` as immutable because it is also borrowed as mutable
  --> <anon>:19:20
   |
18 |     let aref = dat.reference_to_a();
   |                --- mutable borrow occurs here
19 |     println!("{}", dat.get_b());
   |                    ^^^ immutable borrow occurs here
20 | }
   | - mutable borrow ends here

Why is this? I would have thought that the mutable borrow of dat is converted into an immutable one when reference_to_a() returns, because that function only returns an immutable reference. Is the borrow checker just not clever enough yet? Is this planned? Is there a way around it?

like image 782
Timmmm Avatar asked Dec 16 '16 18:12

Timmmm


1 Answers

Lifetimes are separate from whether a reference is mutable or not. Working through the code:

fn reference_to_a(&mut self) -> &i32

Although the lifetimes have been elided, this is equivalent to:

fn reference_to_a<'a>(&'a mut self) -> &'a i32

i.e. the input and output lifetimes are the same. That's the only way to assign lifetimes to a function like this (unless it returned an &'static reference to global data), since you can't make up the output lifetime from nothing.

That means that if you keep the return value alive by saving it in a variable, you're keeping the &mut self alive too.

Another way of thinking about it is that the &i32 is a sub-borrow of &mut self, so is only valid until that expires.

As @aSpex points out, this is covered in the nomicon.

like image 51
Chris Emerson Avatar answered Oct 14 '22 11:10

Chris Emerson