Rust中两个相似异步函数为何async_trait下Wasm实现报线程安全错误
核心错误原因
你单独编写的run函数可以正常编译,但是适配trait后编译失败,核心原因是#[async_trait]宏的默认约束:
- 默认情况下
#[async_trait]会为trait中的异步方法生成的Future默认加上+ Send约束,你原生端使用reqwest的异步Future满足Send要求,因此原生实现编译正常。 - Wasm端的
JsFuture、所有web-sys包装的JS对象(Request、Response、JsValue等)都没有实现Sendtrait:浏览器环境下Wasm运行在单线程事件循环中,这些JS对象绑定到当前主线程,不允许跨线程传递,自然不满足Send约束,因此触发编译错误。
解决方案
最简单的方案是修改trait定义的async_trait参数,取消默认的Send强制约束:
// Trait定义时添加?Send参数,表示不强制要求返回的Future实现Send #[async_trait(?Send)] pub trait FetchRequest { async fn get(&mut self) -> Result<serde_json::Value>; } // Wasm端实现同步使用#[async_trait(?Send)] #[async_trait(?Send)] 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).unwrap(); 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(); // 注意修复原有笔误:应该从json转换而不是resp let parsed: serde_json::Value = json.into_serde().unwrap(); Ok(parsed) } } // 原生端实现也同步使用#[async_trait(?Send)],原有逻辑无需修改 #[async_trait(?Send)] 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()) } }
?Send约束仅表示“Future不强制要求实现Send”,不会影响原生端的使用:原生端的Future本身还是满足Send要求,在原生多线程环境下依然可以正常跨线程传递Future。
内容的提问来源于stack exchange,提问作者stevs
相关产品推荐
相关产品推荐

