如何包装返回Future的闭包且无需其实现Sync trait?
Rust异步闭包包装的Send约束问题修复
问题代码
use std::future::Future; use std::pin::Pin; pub type BoxedOperation = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + 'static>; fn create_func<L, R>(func: L) -> BoxedOperation where L: Fn() -> R + Clone + Send + 'static, R: Future<Output = ()> + Send + 'static { Box::new(move || { let func = func.clone(); Box::pin(async move { // My logic before (func)().await; // In the future, func will receive params // my logic after }) }) }
编译错误信息
--> src/main.rs:14:9 | 14 | / Box::pin(async move { 15 | | (func)().await; 16 | | }) | |__________^ future created by async block is not `Send` | note: future is not `Send` as this value is used across an await --> src/main.rs:15:21 | 15 | (func)().await; | ------ ^^^^^^ await occurs here, with `(func)` maybe used later | | | has type `&L` which is not `Send` note: `(func)` is later dropped here --> src/main.rs:15:27 | 15 | (func)().await; | ^ help: consider moving this into a `let` binding to create a shorter lived borrow --> src/main.rs:15:13 | 15 | (func)().await; | ^^^^^^^^ = note: required for the cast from `impl Future<Output = ()>` to the object type `dyn Future<Output = ()> + Send` help: consider further restricting this bound | 9 | where L: Fn() -> R + Clone + Send + 'static + std::marker::Sync, | +++++++++++++++++++
核心问题分析
编译器报错的根源是:async块生成的Future未实现Send。
在(func)().await这一行,Rust会先借用func生成&L类型的引用,而await会暂停Future执行,这个引用会被跨await保留。由于L没有Sync约束(Sync的定义是T是Sync当且仅当&T是Send),&L无法保证Send,导致整个Future不满足Send要求,无法用于tokio::spawn这类需要Send Future的场景。
修复代码
按照编译器提示,将闭包调用结果提前绑定到变量,缩短引用生命周期:
use std::future::Future; use std::pin::Pin; pub type BoxedOperation = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + 'static>; fn create_func<L, R>(func: L) -> BoxedOperation where L: Fn() -> R + Clone + Send + 'static, R: Future<Output = ()> + Send + 'static { Box::new(move || { let func = func.clone(); Box::pin(async move { // My logic before let future = func(); // 提前调用闭包,获取Future,此时func的引用生命周期结束 future.await; // my logic after }) }) }
修复原理
拆分func()调用和await操作后:
func()调用会消耗clone后的func实例,引用的生命周期仅局限于这一行,不会延伸到await阶段;- 后续await的是
future变量,其类型R已被约束为Send + 'static,因此整个async块生成的Future自然满足Send约束,无需给L添加Sync限制。
内容的提问来源于stack exchange,提问作者maor10
相关产品推荐
相关产品推荐

