Search code examples
rustmove-semanticswasm-bindgenyew

How to create a request and use the data from an async request?


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
}

Solution

  • 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
        </>}
    }