I have this struct
:
#[wasm_bindgen]
pub struct App {
animation_id: i32,
game: Game,
canvas: HtmlCanvasElement,
context: CanvasRenderingContext2d,
images: HashMap<ImageKey, HtmlImageElement>,
}
I'm trying to attach a closure as a click
event listener to the canvas
member. This closure needs to call mutable methods on the game
member, but I'm not sure how to go about that.
Here's what I'm trying to do:
fn attach_event_listeners(app: &App) {
{
let el = app.canvas.clone();
let cb = Closure::<dyn FnMut(_)>::new(move |event: MouseEvent| {
let rect = el.get_bounding_client_rect();
let x = event.client_x() - rect.left() as i32;
let y = event.client_y() - rect.top() as i32;
let x_tile = (x / PIXELS_PER_CELL as i32) as i8;
let y_tile = (y / PIXELS_PER_CELL as i32) as i8;
let index = app.game.get_cell_index(x_tile, y_tile).unwrap(); /* PROBLEM */
let cell = game.as_mut().unwrap().cells().get(index).unwrap(); /* PROBLEM */
app.game.reveal_cell(x_tile, y_tile).unwrap(); /* PROBLEM */
event.prevent_default();
});
app.canvas
.add_event_listener_with_callback("click", cb.as_ref().unchecked_ref())
.unwrap();
cb.forget();
}
}
This obviously doesn't work because these closures use the move
keyword to move ownership. I've been looking through the examples in the web_sys
crate's repo, and it looks like I might need to wrap it in Rc
and Cell
smart pointers as an escape hatch to get around some of the borrowing rules in this case. Is this correct?
I get the feeling that I'm going about this wrong.
EDIT: I think I got it after another day of researching, and re-reading the Rust book's section on Rc
. I need to get better at Cell<T>
and RefCell<T>
. Regular references and Box<T>
(mutable and immutable) make a lot of sense. Rc
makes sense too, but it's difficult to understand if I'm using them correctly in practice. Cell<T>
and RefCell<T>
concern me because they seem to effectively nullify core borrow-checking principles as safe wrappers around unsafe
implementations. That said, it seems like a useful escape hatch in this case because of the Rust-to-JS/DOM interop that I'm trying to do.