1

I've been struggling with trying to understand how to properly annotate lifetimes in my application. I've simplified the actual code I had to this:

struct Item<'a> {
    rf: &'a i32,
}

impl<'a> Item<'a> {
    fn new(main: &'a App) -> Item<'a> {
        Item{
            rf: &main.i
        }
    }
}

struct App<'a> {
    i: i32,
    vec: Vec<Item<'a>>
}

impl<'a> App<'a> {
    fn new() -> App<'a> {
        App {
            i: 32,
            vec: vec![]
        }
    }
    
    fn init(&mut self) {
        self.vec.push(Item::new(self))
    }
    
    fn update(&self) {
        for item in self.vec.iter() {
            println!("{}", item.rf)
        }
    }
}


fn main() {
    let app = App::new();
    app.init();
    app.update();
}

So there's a vector of items that hold a reference to something in App, which I know would exist as long as the app is alive, but the borrow-checker doesn't accept this. Could someone explain what's wrong with this code and how I could fix it?

You can find this code in rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ee8980c9e1b2a0622548525dbcf9f50f

Herohtar
  • 5,347
  • 4
  • 31
  • 41
user2443626
  • 93
  • 1
  • 7
  • 5
    Having the `App` store the vector of items that reference something else in `App` creates a *self-referential struct*, which is *problematic*. See: [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/2189130) – kmdreko Oct 16 '21 at 22:02
  • 2
    Perhaps you can update your question a bit more to explain why the `Item` should contain references to the `App`? Then someone may be able to suggest an alternative pattern. – harmic Oct 17 '21 at 01:43

1 Answers1

1

I think the problem lays with how rust handles references and infers them. Here's a possible solution.

In short, we tell the rust compiler that in the App struct the i member lives for 'a length. This member we can then share with another struct that lives for at least 'a. we achieve this by telling the Item struct when creating it, that it at least has to live for at least the lifetime of the App struct. Because the compiler can be a bit picky about inferring and anonymous lifetimes we have to be explicit. We do this by adding a 'b to the new method of the Item struct. When we then call this method with the lifetime 'a of the app struct, the compiler knows that Item lives for as long as App. Sorry for the short explanation cause more than I probably know goes on here.

struct Item<'a> {
    rf: &'a i32,
}

impl<'a> Item<'a> {
    fn new<'b>(main: &'b App<'a>) -> Item<'a> {
        Item{
            rf: main.i
        }
    }
}

struct App<'a> {
    i: &'a i32,
    vec: Vec<Item<'a>>
}

impl<'a> App<'a> {
    fn new() -> App<'a> {
        App {
            i: &32,
            vec: vec![]
        }
    }
    
    fn init(&mut self) {
        let item = Item::new(self);
        self.vec.push(item)
    }
    
    fn update(&self) {
        for item in self.vec.iter() {
            println!("{}", item.rf)
        }
    }
}


fn main() {
    let mut app = App::new();
    app.init();
    app.update();
}
Robin
  • 26
  • 1