Passing mutable references through multiple functions


#1

Hello!

I am wrestling with the borrow checker and I could use some advice. I think what I’m trying to do is safe, but I don’t know how to convince the borrow checker that it is valid. From my understanding, an omniscient borrow checker would know that the two calls to f in the following code are safe:

use std::marker::PhantomData;

struct Inner<'a> {
    _phantom: PhantomData<&'a ()>,
}
impl<'a> Inner<'a> {
    fn do_a_thing(&mut self) {}
}

struct Outer<'a> {
    x: &'a mut Inner<'a>,
}
impl<'a> Outer<'a> {
    fn act_on_inner(&mut self) {
        self.x.do_a_thing();
    }
}

fn f<'a>(x: &'a mut Inner<'a>) {
    let mut outer = Outer { x: x };
    outer.act_on_inner();
}

fn main() {
    let mut x = Inner {
        _phantom: PhantomData,
    };
    f(&mut x);
    f(&mut x);
}

However, the error is:

error[E0499]: cannot borrow `x` as mutable more than once at a time
  --> src/main.rs:29:12
   |
28 |     f(&mut x);
   |            - first mutable borrow occurs here
29 |     f(&mut x);
   |            ^ second mutable borrow occurs here
30 | }
   | - first borrow ends here

The most unusual part of the above code’s style would be the use of the struct Outer to wrap the Inner in the body of f. I did that for developer ergonomics (I’m writing an API).

I’ve tried a number of things, including enabling non-lexical lifetimes in the nightly compiler, but no fix seems to keep all functionality. I did manage to get the code to typecheck by adding multiple lifetimes to the arguments of f, and deleting its function body:

fn f2<'a, 'b>(x: &'a mut Inner<'b>) {
  // let mut outer = Outer { x: x };
  // outer.act_on_inner();
}

But, as soon as I uncommented the function body, I got this error:

error[E0623]: lifetime mismatch
  --> src/main.rs:25:36
   |
24 |     fn f2<'a, 'b>(x: &'a mut Inner<'b>) {
   |                      -----------------
   |                      |
   |                      these two types are declared with different lifetimes...
25 |         let mut outer = Outer { x: x };
   |                                    ^ ...but data from `x` flows into `x` here

I’ve also experimented with adding lifetime bounds to various parameters, but nothing has worked. (Which is not surprising, since it seems that my mental model is missing a key concept.)

My best educated guess about what is happening (in the original code) is that the struct Outer has a mutable reference to Inner that is living longer than the duration of the function f.

Any help would be greatly appreciated. Thank you!

P.S. Is there any way to turn on debug output to see all lifetimes, included elided ones?

Here is a playground link: https://play.rust-lang.org/?gist=9854d989a2d010466ec7fa0f50b8e7d6&version=stable&mode=debug


#2

It needs to use a second lifetime or the borrow will be bound to the longer period.

struct Outer<'a, 'b> where 'a:'b {
    x: &'b mut Inner<'a>,
}
impl<'a,'b> Outer<'a,'b> {...}
fn f<'a, 'b>(x: &'b mut Inner<'a>) {