You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust/Wasm_bindgen并发调用send函数遇unsafe aliasing错误如何解决?

问题

我正在开发首个Rust/wasm_bindgen项目,使用web_sys向其他服务发起请求。我有多个如下所示的控制器,它们从JS接收请求参数后调用send函数发起请求:

pub async fn get_some_data(&mut self, req_param: String) -> Result<JsValue, JsValue> {
    let req_param = String::from(req_param);
    let request_endpoint = "some_endpoint";
    let request_body = json!({ "req_param": req_param });
    let json = self.send(request_endpoint.to_string(), request_body.to_string()).await?;
    Ok(json)
} 

以下是send函数的实现:

pub async fn send(&mut self, request_endpoint: String, request_body: String) -> Result<JsValue, JsValue> {
    let window = window().ok_or_else(|| JsValue::from_str("Failed to get window object"))?;

    // prepare params ...

    let mut req = RequestInit::new();
    req.method("POST");
    req.mode(RequestMode::Cors);
    req.body(Some(&JsValue::from_str(&request_body.to_string())));

    let request = Request::new_with_str_and_init(&url, &req)?;
    request.headers().set("Content-type", "application/json")?;
    request.headers().set("Accept", "text/plain")?;

    let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
    let response: Response = resp_value.dyn_into()?;

    // check API response
    if !response.ok() {
        return Err(JsValue::from_str(&format!("HTTP error response: {}",response.status())));
    }

    // parse API response as JSON and return
    let json = JsFuture::from(response.json()?)
        .await?;
    Ok(json)
}

随着控制器数量增加,我遇到了错误:
recursive use of an object detected which would lead to unsafe aliasing in rust

我认为该错误是由于send函数被并发调用导致的——如果在JS中用setTimeout延迟其中一个控制器的调用,错误就会消失。请问如何让send函数能够处理并发请求?

解决方案

这个错误的核心原因是:send函数声明了&mut self,但并发调用时多个异步任务会同时持有对self的可变引用,这违反了Rust的可变引用独占规则,wasm_bindgen检测到这种不安全的别名情况后抛出了错误。根据你的实际需求,可以用以下两种方式解决:

场景1:send函数无需修改self状态

如果send函数里并没有实际修改self的成员变量(比如你的示例代码里,send只是发起请求,没有操作self的状态),直接把&mut self改成&self即可:

pub async fn send(&self, request_endpoint: String, request_body: String) -> Result<JsValue, JsValue> {
    // 原代码逻辑完全不变
}

Rust允许同时存在多个不可变引用,这样多个并发请求就能正常执行了。

场景2:send函数需要修改self状态

如果send必须修改self的状态(比如维护请求计数器、共享配置等),需要用线程安全的同步原语保护共享状态。在wasm环境中,可以使用Arc<Mutex<...>>来包裹需要修改的成员:

  1. 重构结构体,用Arc+Mutex包装可变状态:
use std::sync::{Arc, Mutex};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct ApiController {
    // 示例:用Arc+Mutex包裹需要修改的请求计数器
    request_count: Arc<Mutex<u32>>,
    // 其他不需要修改的成员可以保留原样
}

#[wasm_bindgen]
impl ApiController {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        ApiController {
            request_count: Arc::new(Mutex::new(0)),
        }
    }
}
  1. 修改send函数,使用&self并通过lock获取可变访问权:
pub async fn send(&self, request_endpoint: String, request_body: String) -> Result<JsValue, JsValue> {
    // 锁定计数器并修改状态,注意操作完成后提前释放锁
    let mut count = self.request_count.lock().map_err(|_| {
        JsValue::from_str("Failed to lock request counter")
    })?;
    *count += 1;
    drop(count); // 提前释放锁,避免await期间阻塞其他请求

    // 原请求逻辑不变
    let window = window().ok_or_else(|| JsValue::from_str("Failed to get window object"))?;

    let mut req = RequestInit::new();
    req.method("POST");
    req.mode(RequestMode::Cors);
    req.body(Some(&JsValue::from_str(&request_body)));

    // 补充示例URL拼接(原代码中url变量未定义)
    let url = format!("https://your-api-domain/{}", request_endpoint);
    let request = Request::new_with_str_and_init(&url, &req)?;
    request.headers().set("Content-type", "application/json")?;
    request.headers().set("Accept", "text/plain")?;

    let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
    let response: Response = resp_value.dyn_into()?;

    if !response.ok() {
        return Err(JsValue::from_str(&format!("HTTP error response: {}", response.status())));
    }

    let json = JsFuture::from(response.json()?).await?;
    Ok(json)
}

关键提醒:一定要在await调用前释放Mutex锁(比如用drop(count)),否则其他并发请求会被阻塞,失去并发处理的意义。

内容的提问来源于stack exchange,提问作者Pypax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 23:42:12