0

I'm pretty new to rust, trying to make a small game with web assembly using rust and wasm-bindgen. I have an event listener which listens for key presses, and returns a direction through a Stream. Then I want to draw something to a canvas element every 500ms, based on the value of the direction variable.

My problem is that I'm unable to mutate the direction variable from the async block, and use it in the Interval closure.

Using the move keyword in both the async block and the Interval closure makes the code compile, but the direction is then never changed inside the interval function. I think the direction variable is then copied into the block/closure, as the Direction enum implements the Copy trait.

I've included a simplified version of my entry point function:

#[wasm_bindgen]
pub fn run() -> Result<(), JsValue> {
    let mut direction = Direction::Right;

    let fut = async {
        let mut on_key_down = EventListenerStruct::new();

        while let Some(dir) = on_key_down.next().await {
            direction = dir;
          // ^^^^^^^^ this errors because direction does not live long enough
          // argument requires that `direction` is borrowed for `static`
        }
    };
    spawn_local(fut);

    Interval::new(500, || {
        // I want to use direction here
    })
    .forget();

    Ok(())
}

My question is; Can I mutably borrow a variable into an async block? And can I make it live long enough without taking ownership of it?

Thanks in advance,

Christian Fosli
  • 1,646
  • 9
  • 14

1 Answers1

1

Yes, you can do this using Arc and Mutex.

use std::sync::{Arc, Mutex};

fn main() {
    let mut direction = Arc::new(Mutex::new(Direction::Right));

    let direction2 = Arc::clone(&direction);
    let fut = async {
        let mut on_key_down = EventListenerStruct::new();

        while let Some(dir) = on_key_down.next().await {
            *direction2.lock().unwrap() = dir;
        }
    };
    spawn_local(fut);

    Interval::new(500, || {
        let direction = direction.lock().unwrap();
    })
    .forget();
}
Mubelotix
  • 81
  • 4
  • Thank you, this worked wonderfully. I did add a `move` keyword to the async block, to prevent a compilation error "direction2 does not live long enough", but the `direction` variable was still updated for the `Interval`. – Christian Fosli Apr 22 '20 at 17:22