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

无法将闭包传入Hyper::service_fn,如何保留route为ApiServer成员函数?

解决Hyper微服务的编译错误:保留route作为成员函数的方案

我来帮你修复这个编译错误,同时保留route作为ApiServer的成员函数。先拆解一下你遇到的问题:

错误根源

你碰到的E0271类型不匹配错误,本质是所有权与生命周期的冲突:

  • 你在main里创建了一个可变的ApiServer实例,然后试图在make_service_fn的闭包里捕获它的可变引用。
  • Hyper的make_service_fn会为每个新连接生成独立的服务实例,而持有可变引用的闭包无法满足Hyper对服务的Send + Sync要求(服务器是异步多线程的,服务闭包需要能安全地在线程间传递);同时可变引用只能被持有一次,无法为多个连接复用,最终导致类型匹配失败。

下面给出两种可行的修改方案,取决于你的ApiServer是否需要跨连接共享状态:


方案1:不需要跨连接共享状态(最简单)

如果你的ApiServer不需要在多个请求之间保存状态,直接在每个连接的服务闭包里创建新的ApiServer实例即可:

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use std::{convert::Infallible, net::SocketAddr};

struct ApiServer {}

impl ApiServer {
    pub async fn route(&mut self, req: Request<Body>) -> Result<Response<Body>, Infallible> {
        let mut response = Response::new(Body::empty());
        *response.body_mut() = req.into_body();
        Ok(response)
    }
}

#[tokio::main]
async fn main() {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    let make_svc = make_service_fn(|_conn| async {
        // 为每个连接创建独立的ApiServer实例
        let mut api = ApiServer {};
        Ok::<_, Infallible>(service_fn(move |req| async {
            api.route(req)
        }))
    });

    let server = Server::bind(&addr).serve(make_svc);
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

这样每个连接都有自己的ApiServer,完全避免了可变引用的捕获问题,编译器可以顺利通过。


方案2:需要跨连接共享状态

如果你的ApiServer需要在多个请求之间共享状态(比如请求计数、缓存数据等),就需要用Arc<Mutex>来包装实例,实现线程安全的共享可变状态:

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use std::{convert::Infallible, net::SocketAddr, sync::{Arc, Mutex}};

struct ApiServer {
    // 示例:共享状态字段
    request_count: u32,
}

impl ApiServer {
    pub async fn route(&mut self, req: Request<Body>) -> Result<Response<Body>, Infallible> {
        // 更新共享状态
        self.request_count += 1;
        println!("Received request #{}", self.request_count);
        
        let mut response = Response::new(Body::empty());
        *response.body_mut() = req.into_body();
        Ok(response)
    }
}

#[tokio::main]
async fn main() {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    // 用Arc<Mutex>包装ApiServer,实现线程安全的共享
    let api = Arc::new(Mutex::new(ApiServer { request_count: 0 }));

    let make_svc = make_service_fn(move |_conn| {
        // 克隆Arc,每个连接的服务持有独立的引用
        let api = api.clone();
        async move {
            Ok::<_, Infallible>(service_fn(move |req| {
                // 再次克隆Arc,每个请求持有引用
                let api = api.clone();
                async move {
                    // 锁定Mutex获取可变引用(实际项目中建议处理lock可能的错误)
                    let mut api = api.lock().unwrap();
                    api.route(req)
                }
            }))
        }
    });

    let server = Server::bind(&addr).serve(make_svc);
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

关键处理说明:

  1. Arc允许在多个线程间共享所有权,Mutex保证同一时间只有一个线程能修改ApiServer实例。
  2. 在make_service_fn和service_fn中分别克隆Arc,确保每个连接和请求都持有合法的引用,避免所有权冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 13:22:55