Search code examples
rustwasm-bindgen

type mismatch resolving <impl std::future::Future as std::future::Future>::Output == std::result::Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>


I am trying to implement an API class using wasm_bindgen with asynchronous calls.

#![allow(non_snake_case)]

use std::future::Future;

use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use js_sys::Promise;
use web_sys::{Request, RequestInit, RequestMode, Response};
use wasm_bindgen_futures::future_to_promise;

use crate::fetch_example::*;


#[wasm_bindgen]
#[derive(Debug, Serialize, Deserialize)]
pub struct API {
    root: String,
}


#[wasm_bindgen]
impl API {
    pub fn new(root: &str) -> Self {
        Self {
            root: root.to_string(),
        }
    }

    pub fn getVersion(&self) -> Promise {
        let url = format!("{}/version", self.root);

        future_to_promise(async move {
            _request_json(&url, "GET")
        })
    }

    // other methods ...
}


// What is the correct returned type instead of Result<JsValue, JsValue> ???
async fn _request_json(url: &str, method: &str) -> Result<JsValue, JsValue> {
    let mut opts = RequestInit::new();
    opts.method(method);
    opts.mode(RequestMode::Cors);

    let request = Request::new_with_str_and_init(&url, &opts)?;

    request.headers().set("Accept", "application/json")?;

    let window = web_sys::window().unwrap();
    let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;

    let resp: Response = resp_value.dyn_into().unwrap();

    let json = JsFuture::from(resp.json()?).await?;

    Ok(json)
}

As it is seen, I took out the HTTP call into a separate private function _request_json to avoid code duplication in different API methods (they are supposed to be several, not getVersion only).

I took the base example of an HTTP call from here: https://rustwasm.github.io/wasm-bindgen/examples/fetch.html

Also I found that asynchronous calls of struct methods should be implemented with the help of future_to_promise: https://github.com/rustwasm/wasm-bindgen/issues/1858

So the issue is with the returned type by _request_json. I cannot guess it correctly.

When I compile the project I get the error:

type mismatch resolving `<impl std::future::Future as std::future::Future>::Output == std::result::Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>`

If I copy the body of _request_json inside of future_to_promise everything works fine.

What is the correct returned expression for _request_json to make the code works?


Solution

  • This construction:

    async move {
        _request_json(&url, "GET")
    }
    

    has the type impl Future<Output = impl Future<Output = Result<JsValue, JsValue>>>.

    You can fix it like this:

    future_to_promise(async move {
        _request_json(&url, "GET").await
    })
    

    and probably also like this:

    future_to_promise(_request_json(&url, "GET"))