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

Actix-Web服务器与异步闭包间共享状态的报错及解决问询

Actix-web 异步共享状态编译错误分析与修复

问题背景

想要通过异步reqwest定期拉取数据,转换格式后通过Actix-web的HTTP端点对外提供服务,结合Actix状态管理与Rust线程共享状态示例实现时,遇到多轮编译错误。

错误原因解析

第一轮错误(E0308、E0277)

代码中*app_data.status = res.unwrap();存在两个问题:

  • status是String类型,解引用*app_data.status会得到str(String自动Deref到str),而str是动态大小类型(DST),无法直接赋值。
  • 右侧的res.unwrap()是String类型,与左侧的str类型不匹配。

第二轮错误(E0594、E0507)

  • E0594:错误地将web::Data嵌套在Arc<Mutex<...>>内部,而web::Data本身已经是基于Arc的线程安全包装,额外嵌套导致无法通过Arc修改内部数据(Arc未实现DerefMut)。
  • E0507:HttpServer::new的闭包是Fn类型(会被多次调用,比如启动工作线程),move闭包会将app_data2一次性移动进去,后续调用时变量已不存在,导致移动错误。

第三轮错误(E0507)

本质和第二轮的E0507一致:app_data2是Arc<Mutex<Data<AppState>>>类型,未实现Copy,move闭包只能移动一次,无法满足HttpServer多次调用闭包的需求。

修复方案与正确代码

核心调整思路:

  1. 调整状态层级:让web::Data直接包裹Arc<Mutex<AppState>>,利用web::Data的线程安全特性,同时通过Mutex实现可变访问。
  2. 正确修改状态:锁定Mutex后直接修改内部字段,无需重新创建web::Data。
  3. 解决闭包移动问题:在HttpServer闭包里每次cloneweb::Data(轻量操作,内部是Arc的引用计数增加)。

修复后的完整代码:

use actix_web::{get, http, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};

struct AppState {
    status: String,
}

#[get("/")]
async fn index(data: web::Data<Arc<Mutex<AppState>>>) -> impl Responder {
    let state = data.lock().unwrap();
    HttpResponse::Ok()
        .insert_header(http::header::ContentType::plaintext())
        .body(state.status.to_owned())
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let initial_status = get_state().await.unwrap();
    // 正确的状态结构:web::Data包裹线程安全的可变状态
    let shared_state = Arc::new(Mutex::new(AppState {
        status: initial_status,
    }));
    let app_data = web::Data::new(shared_state.clone());

    // 启动定时更新任务
    actix_web::rt::spawn(async move {
        loop {
            println!("执行定时更新...");
            sleep(Duration::from_millis(2000)).await;

            match get_state().await {
                Ok(new_status) => {
                    let mut state = shared_state.lock().unwrap();
                    state.status = new_status;
                    println!("状态已更新");
                }
                Err(e) => eprintln!("获取状态失败: {}", e),
            }
        }
    });

    // 闭包里每次clone web::Data,确保每个工作线程都能拿到共享状态引用
    HttpServer::new(move || {
        App::new()
            .app_data(app_data.clone())
            .service(index)
    })
    .bind(("127.0.0.1", 9090))?
    .run()
    .await
}

async fn get_state() -> Result<String, Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let status = client.get("http://ipecho.net/plain")
        .send()
        .await?
        .text()
        .await?;
    println!("拉取到状态: {status}");
    Ok(status)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 00:45:55