`FnOnce` 的实现对于异步递归不够通用

Implementation of `FnOnce` is not general enough with async recursion

我正在尝试用 Rust 编写几个递归异步函数。我创建了一个我面临的问题的最小示例:

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(())
}

当使用映射到同一个 tokio::spawn 调用的字符串引用数组时,它工作正常:

futures::stream::iter(
    [&String::from("hi")].map(|it: &String| tokio::spawn(bar(it.clone()))),
)

当使用 HashMap 的键时,它在 #[async_recursion] 宏中给出以下编译器错误:

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,)>`

我不太了解生命周期,我猜这与 HashMap 的键寿命不够长或其他原因有关。我该如何解决这个错误?

如果你切换到

futures::stream::iter(
    map.into_keys()
        .map(|it: String| tokio::spawn(bar(it.to_owned()))),
)

您的代码将有效。

你的原始代码不起作用的原因是 rust 不知道

.buffer_unordered(2)
.collect::<Vec<_>>()
.await;

强制生成的期货在您的 HashMap 超出范围之前完成。