Hyper服务报`FnOnce`实现不够通用 生命周期问题排查
FnOnce is not general enough 报错根本原因 该错误是典型的高阶生命周期约束不匹配问题:
- hyper的
make_service_fn要求传入的服务构造闭包必须满足for<'r> FnMut(&'r AddrStream)约束,也就是闭包需要能接受任意生命周期的AddrStream借用参数,适配hyper运行时每次接收到新连接时临时生成的不同生命周期的引用。 - 当你手动给闭包参数标注固定生命周期
'a时,相当于把闭包可接受的引用生命周期限定为某一个固定的作用域长度,无法覆盖运行时所有可能的生命周期场景,编译器因此判定闭包实现不够通用。你后续看到的「期望for<'r> FnMut<(&'r AddrStream,)>、实际得到FnMut<(&'a AddrStream,)>」就是该问题的直接提示。
正确生命周期标注方式
- 不要给传入
make_service_fn的闭包参数绑定外部定义的固定生命周期,只需要标注参数类型,让编译器自动推导高阶生命周期即可。 - 定义Listener的
start方法时,不要把方法自身的生命周期参数和闭包参数关联,避免把闭包的生命周期绑死在方法调用的作用域上。 - 如果需要在服务构造闭包中使用Listener的内部字段,不要通过借用
self的方式捕获,优先将需要共享的字段用Arc包裹后克隆进闭包,避免短生命周期借用导致的约束不匹配。
错误写法示例
// 错误:将闭包参数生命周期绑定到方法的固定生命周期'a impl Listener { pub async fn start<'a, F, Fut>(&'a self, handler: F) -> std::io::Result<()> where F: Fn(Request<Body>) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Response<Body>, Infallible>> + Send, { // 这里手动标注&'a AddrStream直接绑死了生命周期 let make_svc = make_service_fn(|_conn: &'a AddrStream| async move { Ok::<_, Infallible>(service_fn(handler.clone())) }); // hyper::Server::bind(&addr).serve(make_svc).await?; Ok(()) } }
正确写法示例
impl Listener { pub async fn start<F, Fut>(self, handler: F) -> std::io::Result<()> where F: Fn(Request<Body>) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Response<Body>, Infallible>> + Send, { // 不标注固定生命周期,编译器自动推导高阶生命周期约束 let make_svc = make_service_fn(|_conn: &AddrStream| async move { // 需要获取连接信息可直接在闭包内取用,比如拿远程地址: // let peer = _conn.remote_addr(); Ok::<_, Infallible>(service_fn(handler.clone())) }); // hyper::Server::bind(&addr).serve(make_svc).await?; Ok(()) } }
注意:如果
start方法需要保留Listener实例的所有权不被转移,可将Listener内部需要共享的配置提前用Arc包裹,调用时将Arc克隆进闭包即可,不要直接借用&self捕获进闭包。
内容的提问来源于stack exchange,提问作者Silva
相关产品推荐
相关产品推荐

