Game of Life in pure Rust/wasm: avoiding RefCell

Hi folks, I recently went through the WASM book and had a great time. After getting the Game of Life example working well, I decided to port it entirely to Rust, and followed the "building without a bundler" advice to get it running on my website without any JS tooling in sight. This also worked!

I'm at a state now where I'd like to try and cut the WASM binary size:

> cargo bloat --release --crates
    Analyzing target/release/libgame_of_life.so

 File  .text     Size Crate
 8.4%  56.0%  79.9KiB std
 6.1%  40.7%  58.1KiB [Unknown]
 0.2%   1.1%   1.6KiB wee_alloc
 0.1%   0.7%     963B wasm_bindgen
 0.0%   0.0%      18B proc_macro2
15.0% 100.0% 142.6KiB .text section size, the file size is 953.0KiB

Oops, std. That's in there because calling requestAnimationFrame from Rust requires some RefCell magic:

    // Ref tricks to call `request_animation_frame` recursively.
    let f = Rc::new(RefCell::new(None));
    let g = f.clone();
    *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        universe.tick();
        draw_grid(&grid, &universe, &context);
        draw_cells(&alive, &dead, &universe, &context);
        request_animation_frame(&window, f.borrow().as_ref().unwrap());
    }) as Box<dyn FnMut()>));

where I've borrowed this from the WASM guide:

fn request_animation_frame(window: &Window, f: &Closure<dyn FnMut()>) {
    window
        .request_animation_frame(f.as_ref().unchecked_ref())
        .unwrap();
}

Is there a way to accomplish this recursive calling without RefCell, and therefore without std in scope?

Thank you kindly for any guidance you can offer.

RefCell doesn't need std, just core. https://doc.rust-lang.org/core/cell/struct.RefCell.html
And Rc only needs alloc. https://doc.rust-lang.org/alloc/rc/struct.Rc.html

I know nothing specific about the subject but a simple "rust wasm strip" google search yielded https://rustwasm.github.io/wasm-bindgen/reference/optimize-size.html as first link to me.

For copyable things you can use Cell instead of RefCell. Cell is practically zero cost, as it doesn't need to keep or check any state.

use core::cell::RefCell; works fine but use alloc::rc::Rc; doesn't, it claims it can't find that module. Do you know what I'm doing wrong there?

You need to add:

extern crate alloc;

to your crate. You probably want #![no_std] too.

Weird, it worked. I thought extern wasn't necessary anymore?

It's true for almost all cases but alloc is one of the exceptions.
You can read more about it in the reference: https://doc.rust-lang.org/reference/items/extern-crates.html#extern-prelude.

1 Like

Strange, adding alloc like that actually increased the binary size. Either way, I'm still trying to think of a way to avoid this RefCell approach entirely, as opposed to just importing them in a different way.

My approach has been to handle requestAnimationFrame in JS and only call the Wasm code when it is time to render.

FYI here's another approach to request_animation_frame in Rust: https://github.com/rustwasm/gloo/blob/master/rfcs/002-mid-level-raf-api.md

I find this much nicer and idiomatic since it simply cancels on drop.

gloo feels to me like it may have stagnated a bit, so for now I've copied it into my hodgepodge collection of web stuff under the tick feature at awsm_web

That certainly works, and it's how I had it originally while following the WASM guide.

My aim in particular was to see if it was possible to do the entire thing in Rust, and to discover what costs were involved. Overall it went pretty well.

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.