为什么这两个非常相似的异步 Rust 函数之一会触发线程安全错误?

Why does one of these two very similar async Rust functions trigger thread safety errors?

我正在尝试学习 Rust 项目,其中我有一个库,它通过 HTTP 请求特征使用一些 REST API,我计划为本机和 webassembly 使用分别填写,这样我就可以拥有此库在不同环境中的绑定。

我的问题出现在 WASM 部分,我正在尝试调整 fetch example here:

pub async fn run(url: String, authz: String) -> Result<serde_json::Value, JsValue> {
    let mut opts = RequestInit::new();
    opts.method("GET");
    opts.mode(RequestMode::Cors);

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

    request.headers().set("Authorization", &authz)?;

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

    assert!(resp_value.is_instance_of::<Response>());
    let resp: Response = resp_value.dyn_into().unwrap();

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

    let parsed: serde_json::Value = json.into_serde().unwrap();
    Ok(parsed)
}

我在这里做了一些轻微的调整以适应我的目的,但效果很好。不起作用的是我试图将其硬塞到特征界面中。 return 类型是一个 anyhow::Result,我 unwrap() 一堆我不应该在这里暂时避免错误类型兼容性问题的东西:

#[async_trait]
impl FetchRequest for WasmFetchRequest {
    async fn get(&mut self) -> Result<serde_json::Value> {
        let mut opts = RequestInit::new();
        opts.method("GET");
        opts.mode(RequestMode::Cors);

        let request = Request::new_with_str_and_init(&self.path, &opts).unwrap();

        request.headers().set("Authorization", &self.authz);

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

        assert!(resp_value.is_instance_of::<Response>());
        let resp: Response = resp_value.dyn_into().unwrap();

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

        let parsed: serde_json::Value = resp.into_serde().unwrap();
        Ok(parsed)
        /*
        // hoped this might work too, same errors
        Ok(run(
            self.path.to_string(),
            self.authz.to_string()
        ).await.map_err(|err| err.into())?);
        */
    }
}

构建不满意:

error: future cannot be sent between threads safely
  --> src/lib.rs:66:58
   |
66 |       async fn get(&mut self) -> Result<serde_json::Value> {
   |  __________________________________________________________^
67 | |         /*
68 | |         Ok(run(
69 | |             self.path.to_string(),
...  |
91 | |         Ok(parsed)
92 | |     }
   | |_____^ future created by async block is not `Send`
   |
   = help: within `impl Future`, the trait `Send` is not implemented for `Rc<RefCell<wasm_bindgen_futures::Inner>>`
note: future is not `Send` as it awaits another future which is not `Send`
  --> src/lib.rs:83:26
   |
83 |         let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `JsFuture`, which is not `Send`
   = note: required for the cast to the object type `dyn Future<Output = Result<Value, rescale_core::Error>> + Send`

error: future cannot be sent between threads safely
  --> src/lib.rs:66:58
   |
66 |       async fn get(&mut self) -> Result<serde_json::Value> {
   |  __________________________________________________________^
67 | |         /*
68 | |         Ok(run(
69 | |             self.path.to_string(),
...  |
91 | |         Ok(parsed)
92 | |     }
   | |_____^ future created by async block is not `Send`
   |
   = help: within `impl Future`, the trait `Send` is not implemented for `*mut u8`
note: future is not `Send` as this value is used across an await
  --> src/lib.rs:88:20
   |
83 |         let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
   |             ---------- has type `wasm_bindgen::JsValue` which is not `Send`
...
88 |         let json = JsFuture::from(resp.json().unwrap()).await.unwrap();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `resp_value` maybe used later
...
92 |     }
   |     - `resp_value` is later dropped here
   = note: required for the cast to the object type `dyn Future<Output = Result<Value, rescale_core::Error>> + Send`

error: future cannot be sent between threads safely
  --> src/lib.rs:66:58
   |
66 |       async fn get(&mut self) -> Result<serde_json::Value> {
   |  __________________________________________________________^
67 | |         /*
68 | |         Ok(run(
69 | |             self.path.to_string(),
...  |
91 | |         Ok(parsed)
92 | |     }
   | |_____^ future created by async block is not `Send`
   |
   = help: within `Request`, the trait `Sync` is not implemented for `*mut u8`
note: future is not `Send` as this value is used across an await
  --> src/lib.rs:83:26
   |
83 |         let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ first, await occurs here, with `&request` maybe used later...
note: `&request` is later dropped here
  --> src/lib.rs:83:92
   |
83 |         let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
   |                                                                   --------                 ^
   |                                                                   |
   |                                                                   has type `&Request` which is not `Send`
help: consider moving this into a `let` binding to create a shorter lived borrow
  --> src/lib.rs:83:41
   |
83 |         let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: required for the cast to the object type `dyn Future<Output = Result<Value, rescale_core::Error>> + Send`

导致这些错误的相关差异是什么?我想它一定是 &mut self 或者 return 类型的东西,但我无法理解。

可能不相关,但本机版本很适合界面:

#[async_trait]
impl FetchRequest for NativeFetchRequest {
    async fn get(&mut self) -> Result<serde_json::Value> {
        let client = reqwest::Client::new();
        let mut req = client.get(&self.path);
        req = req.header("Authorization", self.authz.as_str());
        let res = req.send().await?;
        res.error_for_status()?
            .json::<serde_json::Value>().await
            .map_err(|err| err.into())
    }
}

根据 async_trait documentation,异步特征方法返回的期货必须默认为 Send:

Async fns get transformed into methods that return Pin<Box<dyn Future + Send + 'async>> and delegate to a private async freestanding function.

你的async fn产生了一个非Send的未来。因此,您的原始代码与使用 async_trait 的代码之间的区别在于,原始代码不 需要 一个 Send 未来,非Send 个,而 async_trait 默认情况下需要 Send 个期货。

要解决此问题,您需要告诉 async_trait not to require Send using #[async_trait(?Send)] on the trait and the impl block. In other words, replace #[async_trait] with #[async_trait(?Send)] in both the trait declaration and the implementation, and your code should compile. (Playground。)