Reputation: 1784
I have the following code, in an attempt to wrap a function that returns a 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
(func)().await; // In the future, func will receive params
// my logic after
})
})
}
This code doesn't compile-
--> 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,
|
+++++++++++++++++++
Now it's critical for the BoxedOperation here to return a future that is Send
so I can tokio::spawn
it later on, and I also don't want it to be Sync as that would require all everything used in func
to be Sync as well. I don't really understand why this isn't Send in the first place.
Upvotes: 3
Views: 1097
Reputation: 71310
This is a tricky case.
When you call func
, the compiler uses Fn::call()
that takes self
by reference. So the compiler borrows func
, creating &L
, and calls this type. But as generator/async functions captures are not precise, this type ends up being included in the resulting future even though it is not needed, and so the resulting future requires &L: Send
-> L: Sync
in order to be Send
.
To fix the problem, you can force the compiler to drop the borrow immediately before awaiting the returned future:
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 {
let fut = { func() }; // Notice the block.
fut.await;
})
})
}
Upvotes: 4