Lifetime will not live long enought while using Arc

For context:

  • I have a trait called GameManager
  • I have some implementations of GameManager, such as SnakeGameManager and TetrisGameManager, ...
  • I want to store mutable reference to GameManager instantiations
  • I have a struct that holds a Vec<Rc<Mutex<dyn GameManager in my main struct as i think is a good way to do that

I want to create a vector to store different instances of these implementations, so i can go from one to the another while still having them both to go back and forth, the think is, the borrow checker is complaining when i try to create a instance of them, here is part of my code:

struct TGamesManager {
    terminal: Terminal<CrosstermBackend<Stdout>>,
    game_instance: Vec<Option<Rc<Mutex<dyn game_manager::GameManager>>>>,
    // Stuff
}
impl TGamesManager {
    fn create_run_game(&mut self, game: Games) -> Result<()> {
        if let Ok(true) = match game {
            Games::Snake => {
                if self.game_instance[self.game_index].is_none() {
                    self.game_instance.insert(
                        self.game_index,
                        Some(Rc::new(Mutex::new(SnakeGameManager::new(&mut self.terminal)))),
                    );
                }
                if let Some(game_instance) = &mut self.game_instance[self.game_index] {
                    game_instance.lock().unwrap().run()
                } else {
                     Ok(false)
                }
            }    
          _ => Ok((false))
        } {
           self.kill_execution = true;
        }
        Ok(()) 
    }
}

The borrow checker says that "Some(Rc::new(Mutex::new(SnakeGameManager::new())))," will not live long enough, how can i fix that ? Disclaimer: i'm new to the rust world and don't know a lot about the rust types

We need more context to understand the problem. Please run cargo check and copy its entire output — not just a single line. Everything the compiler prints is meant to help you, and it'll help us understand what your situation is.

(Also, your code could be simpler if you used Option::get_or_insert_with(). But maybe don't change things till we've figured out what the current situation is.)

    Checking tgames v0.3.1 (/Users/eduardomarinho/Code/Projects/tgames)
error: lifetime may not live long enough
   --> src/tgames.rs:289:30
    |
283 |       fn create_run_game(&mut self, game: Games) -> Result<()> {
    |                          - let's call the lifetime of this reference `'1`
...
289 |                           Some(Rc::new(Mutex::new(SnakeGameManager::new(
    |  ______________________________^
290 | |                             &mut self.terminal,
291 | |                         )))),
    | |___________________________^ cast requires that `'1` must outlive `'static`

error: could not compile `tgames` (bin "tgames") due to 1 previous error

I think there's one important thing I forgot to mention My GameManager accepts an input that has a defined lifetime

pub struct SnakeGameManager<'a> {
       terminal: &'a mut Terminal<CrosstermBackend<Stdout>>,
       // Stuff
}
impl<'a> SnakeGameManager<'a> {
    pub fn new(terminal: &'a mut Terminal<CrosstermBackend<Stdout>>) -> Self {
        SnakeGameManager {
            terminal,
           //Stuff
        }
    }
// Stuff
}

Yes, that is a very important detail. You cannot do that — not and store the SnakeGameManager in TGamesManager; it creates a “self-referential struct” which is not natively supported by Rust (and would not work at all for this purpose, if you want to be able to ever drop the borrow and reuse self.terminal).

For this purpose, you should pass &mut self.terminal to run() instead of storing it inside the SnakeGameManager. As a general principle, you almost never want to put a lifetime parameter on your structs — even though Rust supports it, it's very rarely the right choice.

5 Likes

Thanks for the heads up, i can see why that's the case now. I'm gonna follow your advice removing the lifetimes and passing the self.terminal to run() instead and i'm gonna give a look into Option::get_or_insert_with() as you recommended. Thank you once again!

self.game_instance.insert(
    self.game_index,
    Some(ThingContaining(&mut self.terminal)),
);

If you want more information on why this is an anti-pattern, the reaons include

1 Like

Another thing to consider is that all loans (references to fields and variables) are temporary, limited to their scope, and nothing can change that. Putting a short lived loan in a long-lived Arc will make Arc short-lived and temporary, and not the other way around.

Arc is only useful if it owns it content, and there are no references inside it anywhere.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.