0

I'm working on a function which makes a request to an API, pulls the data, and parses it before returning the data as a Vector of a particular type. Because this is part of a Yew project, tokio and other packages are incompatible. I'm using the wasm-bindgen-futures crate and the spawn_local function from within.

The following is a (skelotonized) failed attempt to complete the relatively simple task. The problem is that the compiler displays is use of moved value: 'games_tonight'. I'm struggling to understand the lower level operations taking place here, and thus the solution to this problem.

pub fn get_sched() -> Vec<MyObj> {

    let mut games_tonight = Vec::new();
    wasm_bindgen_futures::spawn_local(async move {

        let response: myStruct = Request::get(" http://...url...").send().await.unwrap().json().await.unwrap();

        let raw_json = &response.dates[0].games;
        for g in raw_json.to_owned() {
            games_tonight.push(MyObj {
                // LOGIC HERE TO CREATE INSTANCES OF MyObj
            });
        }
    });
    games_tonight
}
cafce25
  • 15,907
  • 4
  • 25
  • 31
Jackie
  • 198
  • 12
  • What happens if you try to remove the `move` in `async move { ... }`? – jthulhu Dec 20 '22 at 20:54
  • @jthulhu then the compiler will complain about lifetimes not living long enough because the future passed to `spawn_local` has to be `'static`. – cafce25 Dec 20 '22 at 20:58
  • @cafce25 ah, you're right. As per [the documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html), `spawn_local` cannot contain references to variables on the stack. I think the available options are global variables, which will be allocated in the data segment, or to allocate on the stack (using `Rc` is the only way that comes to mind without `unsafe`). – jthulhu Dec 20 '22 at 21:09

1 Answers1

1

The problem is that move blocks move all variables referenced in them into their body.

You can just convert the function into an async one:

#[derive(Clone)]
pub struct MyObj {}
pub async fn get_sched() -> Vec<MyObj> {
    let response: myStruct = Request::get("http://...url...")
        .send()
        .await
        .unwrap()
        .json()
        .await
        .unwrap();
    let mut games_tonight = Vec::new();
    let raw_json = &response.dates[0].games;
    for g in raw_json.to_owned() {
        games_tonight.push(MyObj {
                // LOGIC HERE TO CREATE INSTANCES OF MyObj
            });
    }
}

and use spawn_local from within the component that also has access to it's state:

use yew::prelude::*;
#[function_component(ComponentName)]
fn component() -> Html {
    let games = use_state(|| Vec::new());
    wasm_bindgen_futures::spawn_local({
        let games = games.clone();
        async move {
            games.set(get_sched().await);
        }
    });
    html! {<>
        // use games
    </>}
}
cafce25
  • 15,907
  • 4
  • 25
  • 31
  • Seems like the match block is being called prior to the secondary block. Going to try to implement a callback to prevent this behavior. Thank you for the help. – Jackie Dec 22 '22 at 03:42
  • Uhm yea I don't know what I thought yesterday. That previous solution doesn't work because you can't block on the browser. I've edited the answer. See if that helps. @Jackie – cafce25 Dec 22 '22 at 08:57