Is there a better way to store global state?
Opened this issue ยท 7 comments
Hello! Very nice writeup as well for this, and I'm hoping I can help out a bit!
For things like state and such I'd personally recommend doing similar things in wasm that you'd otherwise do when creating C bindings for some Rust code. (that's basically what Rust looks like to JS anyway). In that sense I'd probably recommend allocated state and such. Basically something like:
#[no_mangle]
pub extern fn state_new() -> *mut State {
Box::into_raw(Box::new(State::new()))
}
#[no_mangle]
pub unsafe extern fn state_free(state: *mut State) {
Box::from_raw(state);
}
That way you can package everything up nicely in State
and also maybe even have multiple instances of State
(if you so desired). This'd also skip any mutex locking and such as there's no need for lazy_static
or otherwise convincing Rust you need a mutex.
The downside of this approach, though, is that it's somewhat unsafe! For example binding methods would look like:
#[no_mangle
pub unsafe extern fn state_frob(state: *mut State, argument: i32) -> u32 {
(*state).frob(argument)
}
impl State {
fn frob(&mut self, argument: i32) -> u32 {
// ...
}
}
In addition to the unsafe
keyword and the raw pointer, you also need to be sure to get &mut
vs &self
right because if you call from JS to Rust to JS back to Rust then things can get tricky to keep them correct (this is sort of in the weeds, but important!)
You mentioned that you were glad not using babel/webpack and such, but if you're ok with using various preprocessors I might recommend trying out (or maybe just taking a look at) what wasm-bindgen is doing. I'm hoping to develop wasm-bindgen over time in a way that creates JS apis out of idiomatic Rust APIs (and eventually other languages too). It wraps up all the above unsafety and logic for you so all you do is define methods in Rust and all of a sudden you get methods on JS objects as well.
The wasm-bindgen like solution is currently targeted though at somewhat more heavyweight bindings on the JS side of things (where both JS and wasm have are big and have lots of interop). I've found though that it's great to package up nice errors and smooth out a lot of the idioms.
In any case, curious about what you think of all that!
Hey @alexcrichton, thanks for commenting!
Okay, i think i might have stumbled across another project that was using Box
with global state. What you've described already sounds much cleaner than using a mutex and dealing with locking/unlocking. Do you have any small examples of this pattern being used or perhaps blog posts on it? I definitely need to read through the book still, so I think i'll be able to grasp this more after doing that! I think for my next demo project, i'll attempt to implement my state this way and see how it goes :)
Oh wasm-bindgen sounds really interesting! Generating all the js/wasm glue code definitely would be really helpful. I'm definitely okay with using preprocessors! It was just really refreshing to not have to boot up webpack/babel and to write some raw js for once ๐ I'll keep my eye on wasm-bindgen. Sounds useful for larger projects as you mentioned!
Another project that I'm interested in is stdweb which sounds like their goal is to provide a library of browser apis. Keeping my eye on that one too!
You can also use thread_local!
since you're pretty much guaranteed to have a single thread per WASM instance.
It makes more sense in my use case since I'm coding a Rust app and my JS is just a thin wrapper to run WASM and do some DOM work.
@alvaro-cuesta thanks for that great usage example! Your use case sounds much like my use case for now at least.
I'll have to play around with this pattern vs the Box pattern above.
@jakedeichert keep in mind it uses RefCell
which can panic at runtime just like your Mutex
did.
@alvaro-cuesta thanks for letting me know. I haven't learned about RefCell
yet but did notice its usage within the WasmBlock example you linked to above.
Do you have any small examples of this pattern being used or perhaps blog posts on it?
Ah unfortunately no :(. So far I've mostly been working on glimmerjs/glimmer-vm#752 which is mostly using wasm-bindgen. If you're looking for in general a C API for Rust code though you maybe interested in https://github.com/rust-lang/regex/blob/master/regex-capi/src/rure.rs (a bit large, but complete!) which is a C api for the Rust regex
crate.