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

Rust中trait约束下Future不满足Send trait的编译问题

问题:Axum处理器中使用泛型Trait约束导致Future非Send错误

我有一个JobManager类型,为了通过automock测试依赖对该模块的调用,将核心功能封装到GetOrCreate trait中。直接传递Arc<JobManager>给Axum处理器时代码可正常编译,但换成泛型Trait约束<T: GetOrCreate>后,出现future returned by ... is not Send的编译错误,即使给所有相关类型添加Send + Sync + 'static约束也无法解决。

代码概要

use mockall::automock;
use tokio::sync::Mutex;
use std::collections::HashMap;
use thiserror::Error;
use axum::{Router, routing::get, extract::{State, Path}};
use serde::Deserialize;
use serde_with::{serde_as, hex::Hex};
use std::sync::Arc;

pub struct JobManager {
    jobs: Mutex<HashMap<Job, JobState>>,
}

#[derive(Clone, Error, Debug, PartialEq)]
pub enum JobError {
    #[error("Job creation failed")]
    CreationFailed,
}

#[automock]
pub trait GetOrCreate: Send + Sync + 'static {
    async fn get_or_create(&self, job: Job) -> Result<JobState, JobError>;
}

impl GetOrCreate for JobManager {
    async fn get_or_create(&self, job: Job) -> Result<JobState, JobError> {
        let mut hs = self.jobs.lock().await;
        match hs.get(&job).cloned() {
            Some(j) => Ok(j),
            None => {
                hs.insert(job.clone(), JobState::Created);
                Ok(JobState::Created)
            }
        }
    }
}

impl JobManager {
    pub fn new() -> Self {
        JobManager {
            jobs: Mutex::new(HashMap::new()),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum JobState {
    Created,
    InProgress,
    Done(Result<u64, String>),
}

#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub struct Job {
    pub nonce_group: u8,
    pub challenge: [u8; 8],
    pub difficulty: [u8; 32],
    pub miner: [u8; 32],
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let job_manager = JobManager::new();
    let router = router(Arc::new(job_manager));

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    axum::serve(listener, router).await?;
    Ok(())
}

fn router<T: GetOrCreate + Send + Sync + 'static>(job_manager: Arc<T>) -> Router {
    Router::new()
        .route(
            "/job/:nonce_group/:challenge/:difficulty/:miner",
            get(get_job),
        )
        .with_state(job_manager)
}

#[serde_as]
#[derive(Deserialize)]
struct HexStr<const COUNT: usize>(#[serde_as(as = "Hex")] [u8; COUNT]);

impl<const COUNT: usize> HexStr<COUNT> {
    fn as_array(self) -> [u8; COUNT] {
        self.0
    }
}

async fn get_job<T: GetOrCreate + Send + Sync + 'static>(
    State(manager): State<Arc<T>>,
    Path((nonce_group, challenge, difficulty, miner)): Path<(
        u8,
        HexStr<8>,
        HexStr<32>,
        HexStr<32>,
    )>,
) -> Result<JobState, JobError> {
    manager
        .get_or_create(Job {
            nonce_group,
            challenge: challenge.as_array(),
            difficulty: difficulty.as_array(),
            miner: miner.as_array(),
        })
        .await
}

编译错误信息

error: future cannot be sent between threads safely
   --> src/main.rs:138:17
    |
138 |             get(get_job),
    |                 ^^^^^^^ future returned by `get_job` is not `Send`
    |
= help: within `impl Future<Output = Result<JobState, JobError>>`, the trait `Send` is not implemented for `impl Future<Output = Result<JobState, JobError>>`, which is required by `fn(State<Arc<_>>, axum::extract::Path<(u8, HexStr<8>, HexStr<32>, HexStr<32>)>) -> impl Future<Output = Result<JobState, JobError>> {get_job::<_>}: Handler<_, _>`
note: future is not `Send` as it awaits another future which is not `Send`
   --> src/main.rs:196:5
    |
196 | /     manager
197 | |         .get_or_create(Job {
198 | |             nonce_group,
199 | |             challenge: challenge.as_array().clone(),
200 | |             difficulty: difficulty.as_array().clone(),
201 | |             miner: miner.as_array().clone(),
202 | |         })
    | |__________^ await occurs here on type `impl Future<Output = Result<JobState, JobError>>`, which is not `Send`
note: required by a bound in `axum::routing::get`
   --> /home/acud/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
    |
385 | top_level_handler_fn!(get, GET);
    | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^
    | |                     |
    | |                     required by a bound in this function
    | required by this bound in `get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
help: `Send` can be made part of the associated future's guarantees for all implementations of `GetOrCreate::get_or_create`
    |
41  -         async fn get_or_create(&self, job: Job) -> Result<JobState, JobError>;
41  +         fn get_or_create(&self, job: Job) -> impl std::future::Future<Output = Result<JobState, JobError>> + Send;
    |

解决方法

根据编译器提示,修改GetOrCreate trait的方法定义,将异步方法显式声明为返回带Send约束的Future:

1. 修改Trait定义

#[automock]
pub trait GetOrCreate: Send + Sync + 'static {
    fn get_or_create(&self, job: Job) -> impl std::future::Future<Output = Result<JobState, JobError>> + Send;
}

2. 调整JobManager的实现

将原来的async fn改为普通函数,返回一个异步块:

impl GetOrCreate for JobManager {
    fn get_or_create(&self, job: Job) -> impl std::future::Future<Output = Result<JobState, JobError>> + Send {
        async move {
            let mut hs = self.jobs.lock().await;
            match hs.get(&job).cloned() {
                Some(j) => Ok(j),
                None => {
                    hs.insert(job.clone(), JobState::Created);
                    Ok(JobState::Created)
                }
            }
        }
    }
}

原因说明

当直接使用具体类型Arc<JobManager>时,编译器能推断出其异步方法返回的Future是Send的;但使用泛型Trait约束时,Rust默认不会为Trait中异步方法的返回Future自动添加Send约束,因此需要显式声明,确保所有实现该Trait的类型返回的Future都满足Send要求,适配Axum多线程运行时的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 13:58:09