Port from JavaScript. Questions

To familiarize myself with Rust in 2022 I ported a small Javascript SPA that originates from around 2012. In essence, the goal was, how would I do "X" (functions, xhr, parsing, regex, events, etc.) in Rust?

To get around some scoping issues, I updated the JS as I was doing the Rust port. This was done so I can keep the code near to 1:1 as possible.

The port is done, and it works (both JS and Rust implementations). But I have questions:

The SPA? Reads a text file (with pairs of quoted strings), fetches some data, and creates a slideshow.

Updated JS:

Rust Port:

Questions:

  1. Globals: In Rust it seems like I couldn't create a global object to namespace and store all my values/references. So in both the JS and Rust code I created a custom prototype/type, dumped all my methods/variables/states into that, initialized it for use, and passed that throughout the code. Is there a better/simpler way of holding/referencing mutable values for use in functions other than by passing it around?

  2. Use of static/Atomic (globals): Supposedly static/Atomic is not to be relied on (inefficient ?). Is there a better way of sharing a non-complex mutable value between functions, without passing it around?

  3. Unwrap or Match+OK+Err (verbosity): Earlier versions of the Rust code used Unwrap everywhere. However I have seen suggestions that Match+OK+Err is better. What is the consensus on this?

  4. Closures >>> Cloning (verbosity): Everywhere I closed over a value I ended up having to clone the value in Rust to keep the compiler happy (lifetimes). Is there a way to do closures in Rust without cloning?

  5. Parameter exhaustion. (syntax): For me, the increase in function parameters increased the headache of debugging compiler errors (e.g. passed it once? ha ha, gotta pass it everywhere now). I crammed a lot into my custom type and passed that blob around, but ideally, I would like to pass less stuff, and just reference an 'external' set of types/values/etc. Is that possible?

  6. Sometimes mut, sometimes not (syntax): For me it was odd that some functions were OK with arg: &Type being passed but others needing arg: &mut Type. I realized that the functions that might modify Type required the mut (i.e. contains an if statement), but it seems I'm rewriting/summarizing every function inside the arguments/parameter signature. Is this just the way things are in strongly-typed languages?

Everywhere you use SLIDE_INDEX you have a &mut SlidesT, which makes me think it should be part of that type. But in some place you clone those and stick the clones in some callbacks, so perhaps they're all supposed to share one index, and that's why you made it global? [1]

You could have an Arc<AtomicUsize> instead. It's possible all your data should have shared ownership (e.g. in an Arc<_>).

It's more nuanced than "always A or always B". Here's a recent thread on the topic.

For parse_text in particular, consider implementing FromStr.

Global state is possible but isn't very idiomatic, and you'll pay a different kind of ergonomic cost and perhaps a performance hit.

There's at least some standalone functions that could be methods. There's still a &self parameter in that case but it's less out-of-the-way at the call site. Most things that take your type and some primitives are candidates for this.

fn load_slide(slides: &SlidesT, index: usize) {
fn unload_slide(slides: &SlidesT) {
pub fn get_next(slides: &mut SlidesT, event: &'static str) {
pub fn get_prev(slides: &mut SlidesT, event: &'static str) {
// ...

if doesn't inherently mutate anything so I'm not sure what that was about.

&mut is part of how Rust's owernership and borrowing semantics work; &mut means you have exclusive access to the parameter for the entirety of the function (which is what allows mutation without some form of synchronization). It's needed in the signature because the signature is the contract between the function body and the caller. [2]

You could use Mutex<_> or Arc<Mutex<_>> instead and lock within the function body where required. That would allow you to change things in the function body even though you take a &Type. The ability to do that is called shared mutability or interior mutability.

It looks like the web_sys types use interior mutability and global or external state pervasively, which is probably clouding your experience and potentially my reply too. Programming in this environment may be pretty distinct from other environments one could write Rust for.


  1. There's probably some in-browser coordination or the like going on that I didn't bother unraveling. ↩︎

  2. If it were inferred from the function body instead, borrow checking would require compiling everything and not just checking the signature, and changes to the body would break far away code when the function changed in a way that changed the effective signature. ↩︎

Thank you so much for the response and feedback! As someone new to rust, Arc, Mutex, and macros, was a little over my head, or seemed heavy-handed. I'm sure as I write more code I'll become more comfortable using them.

Thanks for the FromStr link. I think that could have made things simpler. I probably did not look it up at the time because I was converting from JS, method by method.

I was thinking about the functions with if statements (e.g. get_next) that might lead to the instance of the SlidesType being populated or modified (>> load_image >> set_src).

The SLIDE_INDEX represents the current slide the user is on, and varies (increments or decrements) when the user clicks or swipes. Callback is needed because the associated image may not be loaded yet.

There is also a lowercase index in the code, that is used for preloading. Callback is needed as a throttle (load image, wait for success, only then load next ... until last).

I could try to put both (user index, preload index) within the SlidesType and see if I like that better. Right now, SlidesType only has the vectors of strings and images.

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.