New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a better way to store global state? #3

Open
jakedeichert opened this Issue Dec 29, 2017 · 7 comments

Comments

3 participants
@jakedeichert
Owner

jakedeichert commented Dec 29, 2017

No description provided.

@alexcrichton

This comment has been minimized.

alexcrichton commented Dec 29, 2017

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!

@jakedeichert

This comment has been minimized.

Owner

jakedeichert commented Dec 29, 2017

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!

@alvaro-cuesta

This comment has been minimized.

alvaro-cuesta commented Dec 29, 2017

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.

@jakedeichert

This comment has been minimized.

Owner

jakedeichert commented Dec 29, 2017

@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.

@alvaro-cuesta

This comment has been minimized.

alvaro-cuesta commented Dec 29, 2017

@jakedeichert keep in mind it uses RefCell which can panic at runtime just like your Mutex did.

@jakedeichert

This comment has been minimized.

Owner

jakedeichert commented Dec 29, 2017

@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.

@alexcrichton

This comment has been minimized.

alexcrichton commented Dec 30, 2017

@jakedeichert

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment