Reputation: 164
I'm trying to write a couple of recursive async functions in Rust. I've created a minimal example of the problem I'm facing:
use std::collections::HashMap;
use anyhow::Result;
use async_recursion::async_recursion;
use futures::StreamExt;
#[async_recursion]
async fn foo() -> Result<()> {
let map: HashMap<String, String> = HashMap::new();
futures::stream::iter(map.keys().map(|it: &String| tokio::spawn(bar(it.clone()))))
.buffer_unordered(2)
.collect::<Vec<_>>()
.await;
Ok(())
}
#[async_recursion]
async fn bar(it: String) -> Result<()> {
foo().await;
Ok(())
}
When using an array of string references mapped to the same tokio::spawn
call it works fine:
futures::stream::iter(
[&String::from("hi")].map(|it: &String| tokio::spawn(bar(it.clone()))),
)
When using the HashMap's keys, it gives me the following compiler error at the #[async_recursion]
macro:
implementation of `FnOnce` is not general enough
closure with signature `fn(&'0 std::string::String) -> tokio::task::JoinHandle<Result<(), anyhow::Error>>` must implement `FnOnce<(&std::string::String,)>`, for any lifetime `'0`...
...but it actually implements `FnOnce<(&std::string::String,)>`
I'm not very informed about lifetimes, my guess is this has something to do with the HashMap's keys not living long enough or something. How can I fix this error?
Upvotes: 2
Views: 594
Reputation: 8494
If you switch to
futures::stream::iter(
map.into_keys()
.map(|it: String| tokio::spawn(bar(it.to_owned()))),
)
your code will work.
The reason your original code doesn't work is that rust doesn't know that
.buffer_unordered(2)
.collect::<Vec<_>>()
.await;
forces the spawned futures to finish before your HashMap
goes out of scope.
Upvotes: 2