Related to https://github.com/rust-lang/rust/issues/31752. cc @jonhoo
This code does not compile (playground):
extern crate futures_util; // 0.3.4
use futures_util::stream::StreamExt;
struct Foo;
impl Foo {
fn foo(&mut self) { }
}
fn main() {
let mut x = Foo;
futures_util::stream::iter(0..1).for_each(move |_| {
async { x.foo(); }
});
}
due to:
error: captured variable cannot escape `FnMut` closure body
--> src/main.rs:12:9
|
11 | futures_util::stream::iter(0..1).for_each(move |_| {
| - inferred to be a `FnMut` closure
12 | async { x.foo(); }
| ^^^^^^^^^^^^^^^^^^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
It would be helpful if the compiler said which captured variable escaped the closure (in the example, it is x, but this is not always clear if there are many captured variables).
minimized without futures-util crate(playground):
use core::future::Future;
struct Foo;
impl Foo {
fn foo(&mut self) {}
}
async fn bar<T>(x: impl FnMut() -> T)
where
T: Future<Output = ()>,
{
}
fn main() {
let mut x = Foo;
bar(move || async {
x.foo();
});
}
I also came across this error without using move closure:
use std::future::Future;
async fn x<T: Future<Output = ()>>(fun: impl FnMut() -> T) {}
async fn main() {
let mut y = 0;
x(async || {
y += 1;
})
.await;
}
Which errors with:
error: captured variable cannot escape `FnMut` closure body
--> src/main.rs:36:11
|
36 | x(async || {
| ^^^^^^^-
| | |
| | inferred to be a `FnMut` closure
| returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
I'm not sure what the problem is, though. (because I'm still a novice to Rust and haven't fully wrapped my head around the innter workings of Futures yet).
From my understanding, the closure captures y by &mut reference, which is somehow leaked outside of the closure's body.
Is this caused by the expansion of an async closure to a struct holding all the captured variables?
An explanation and a possible workaround would be very appreciated.
Edit:
Even further simplified:
pub async fn main() {
let mut y = 0;
async || y += 1;
}
Nevermind, I figured out what was wrong.
If I were able to define an async closure mutably capturing its environment, it would be possible to invoke the closure multiple times without actually awaiting the future (or dropping it in some other way).
This way, we would get multiple Futures with aliased mutable pointers.
If it is known that this won't happen, then a combination of Rc and RefCell can be used to achieve a safe solution to this problem:
let x = Rc::new(RefCell::new(0));
(async || *(x.borrow_mut()) += 1)().await;
Most helpful comment
minimized without futures-util crate(playground):