Search code examples
rustslint

Use of moved value: `game`


I'm creating a game of 2048 with a UI using Slint. I have a ownership (?) issue when I have 2 different actions that can modify my game. They cannot be called at the same time (but the compiler does not know that). I don't find this kind of issue on the documentation.

mod board;

use board::Board;
use slint::{ModelRc, SharedString, Weak};

slint::include_modules!();

fn update_app(ui: AppWindow, game: &Board) {
    ...
}

fn main() -> Result<(), slint::PlatformError> {
    let mut game = Board::new(32);

    let ui = AppWindow::new()?;
    let ui_handle = ui.as_weak();

    update_app(ui_handle.unwrap(), &game);

    ui.on_restart(move |seed| {
        println!("Restarting with seed: {}", seed);

        game.restart(seed.parse().unwrap());    // if commented, OK
        update_app(ui_handle.unwrap(), &game);  // if commented, OK
    });

    ui.on_play(move |direction| {  // otherwise error here: use of moved value: `game` value used here after move
        println!("Playing with direction: {:?}", direction);

        game.apply_action(direction as u8);
        update_app(ui_handle.unwrap(), &game);
    });

    ui.run()
}

What is the standard approach for this kind of problem? ChatGPT proposed to use a "Reference Counted" but I could not make it work.


Solution

  • The solution has been found using a Reference Counted for the game and duplicate the weak references to the ui (1 per action)

    mod board;
    
    use std::{cell::RefCell, rc::Rc};
    
    use board::Board;
    use slint::{ModelRc, SharedString};
    
    slint::include_modules!();
    
    fn update_app(ui: AppWindow, game: Rc<RefCell<Board>>) {
        ...
    }
    
    fn main() -> Result<(), slint::PlatformError> {
        let game = Rc::new(RefCell::new(Board::new(32)));
        let ui = AppWindow::new()?;
    
        let ui_handle = ui.as_weak();
        let ui_handle_on_play = ui.as_weak();
        let ui_handle_on_restart = ui.as_weak();
    
        let game_on_restart = Rc::clone(&game);
        let game_on_play = Rc::clone(&game);
    
        ui.on_restart(move |seed| {
            game_on_restart.borrow_mut().restart(seed.parse().unwrap());
            update_app(game_on_restart.unwrap(), Rc::clone(&game_on_restart));
        });
    
        ui.on_play(move |direction| {
            game_on_play.borrow_mut().apply_action(direction as u8);
            update_app(ui_handle_on_play.unwrap(), Rc::clone(&game_on_play));
        });
    
        // setup and run the UI
        update_app(ui_handle1.unwrap(), Rc::clone(&game));
        ui.run()?;
        Ok(())
    }