Rust 1.85 stabilized async closures https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/#async-closures
They have nice example, basically
async fn example() {
let mut vec: Vec<String> = vec![];
let mut closure = async || {
vec.push(std::future::ready(String::from("")).await);
};
let res = closure().await;
println!("example: vec = {:?}", vec);
}
However if I make the example just slightly more complex (useful?)
async fn example_modified() {
let mut vec: Vec<String> = vec![];
let data = vec!["1","2","3","4"];
let mut closure = async |d: &str| {
vec.push(std::future::ready(String::from("")).await);
};
let futures = data.into_iter().map(closure); // will not compile because of this line
futures::future::join_all(futures).await;
println!("example: vec = {:?}", vec);
}
I get
error: async closure does not implement `FnMut` because it captures state from its environment
--> src/async_fnmut.rs:69:23
|
69 | let mut closure = async |d: &str| {
| ^^^^^^^^^^^^^^^
...
73 | let futures = data.into_iter().map(closure); // will not compile because of this line
| --- required by a bound introduced by this call
|
note: required by a bound in `std::iter::Iterator::map`
--> /Users/martin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:748:12
|
745 | fn map<B, F>(self, f: F) -> Map<Self, F>
| --- required by a bound in this associated function
...
748 | F: FnMut(Self::Item) -> B,
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
Can you explain why?
The main problem is that the future that your closure returns needs an exclusive reference to vec
so it can push values to it.
To prevent mulitple exclusive references existing in parallel you cannot be allowed to call the closure again before you've awaited the returned future. That contract however isn't enforcable with a FnMut
therefore async closures that capture a mutable reference in their returned future cannot implement FnMut
. They do implement AsyncFnMut
instead which allows to return borrows to the closure.
Iterator::map
requires a FnMut
so you can't use it with that closure.
You can simply wrap that mutable reference in a RefCell
(or Mutex
in multithreaded environments) to get around this limitation:
use std::cell::RefCell;
#[tokio::main]
async fn main() {
let mut vec: Vec<String> = vec![];
let data = vec!["1", "2", "3", "4"];
{
let vec = RefCell::new(&mut vec);
let mut closure = async |d: &str| {
vec.borrow_mut()
.push(std::future::ready(String::from("")).await);
};
let futures = data.into_iter().map(closure);
futures::future::join_all(futures).await;
}
println!("example: vec = {:?}", vec);
}