4

I recently have many problems with the borrow checker of Rust refusing my code. In order to ask the question, I simplified my code:

use std::cell::RefCell;
use std::rc::{Rc, Weak};

struct SomeOtherType<'a>{
    data: &'a i32,
}

struct MyType<'a> {
    some_data: i32,
    link_to_other_type: Weak<RefCell<SomeOtherType<'a>>>,
}

struct ParentStruct<'a> {
    some_other_other_data: i32,
    first_types: &'a Vec<MyType<'a>>,
}


fn get_parent_struct<'a>(first_types: &'a Vec<MyType<'a>>) -> ParentStruct<'a> {
    ParentStruct { some_other_other_data: 4, first_types: first_types }
} 

fn consume(parent_struct: ParentStruct) {
    print!("{}", parent_struct.first_types[0].some_data);
}

fn main() {
    let some_vect = vec!(
        MyType { some_data: 1, link_to_other_type: Weak::new() },
        MyType { some_data: 2, link_to_other_type: Weak::new() }
    );
    loop {
        let some_struct = get_parent_struct(&some_vect);
        consume(some_struct);
    }
}

This code doesn't compile, I have get following error:

error[E0597]: `some_vect` does not live long enough
  --> src/main.rs:33:46
   |
33 |         let some_struct = get_parent_struct(&some_vect);
   |                                              ^^^^^^^^^ borrowed value does not live long enough
...
36 | }
   | - `some_vect` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created 

But the strange fact is: if, in the type MyType, I change Weak<RefCell<...>> to Rc<RefCell<...>> or to RefCell<...> or to Weak<...>: it compiles!!

My question is: why? Why does the borrow checker refuses to compile the original code (and why does it accept the code with the other types instead of Weak<RefCell<...>>)?

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
Truc Truca
  • 79
  • 6
  • 1
    For anyone interested in answering this question, I further [simplified the code](https://play.rust-lang.org/?gist=68cdba0a208bbb4e3afbf63cdbf9ee7b&version=stable&mode=debug) (it still shows the same error) (@Truc Truca, if you want, you can edit your question and use the simplified code instead). So I'm pretty sure it has something to do with the invariance of `RefCell`. `Rc` and `Weak` also contain `Cell`s, maybe that's a hint? I need to stop researching this now, but I'm very curious to see a good explanation for this interesting question! – Lukas Kalbertodt May 02 '18 at 07:56
  • 1
    The solution is to use a different lifetime for the reference: `&'a Vec>` (maybe with `'b:'a`). As for the *why?* I'm not sure and will leave the answer to someone with a better understanding of lifetimes. – MB-F May 02 '18 at 08:16
  • 1
    Also, very related: https://stackoverflow.com/questions/43210387/is-this-error-due-to-the-compilers-special-knowledge-about-refcell – Lukas Kalbertodt May 02 '18 at 12:50

1 Answers1

3

Since Lukas Kalbertodt provided a MCVE (Playground), I'll use his code:

struct MyType<'a> {
    link_to_other_type: Weak<RefCell<&'a i32>>,
}

fn get_parent_struct<'a>(_: &'a MyType<'a>) {} 

fn main() {
    let foo = MyType { link_to_other_type: Weak::new() };
    get_parent_struct(&foo);
}

Let's go through it step by step:

  • A Weak is created and moved into MyType with lifetime 'a
  • When it's passed to get_parent_struct<'a>(_: &'a MyType<'a>), you have a reference with lifetime 'a to a type with lifetime 'a
  • get_parent_struct expects it's parameter to live exactly as long as foo itself, which is not true, since foo lives until the end of the scope

As mentioned by kazemakase, the solution is to use a different lifetime for the reference. If you alter the signature of get_parent_struct slightly, it's working:

fn get_parent_struct<'a>(_: &MyType<'a>) {} 

Playground

The compiler will now elide the lifetimes to

fn get_parent_struct<'a, 'b>(_: &'b MyType<'a>) where 'a: 'b {}

Now 'a outlives 'b.

Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69