异步迭代 BTreeSet 时出现奇怪的生命周期错误

时间:2021-07-08 08:20:39

标签: generics rust async-await lifetime

我希望以下代码中的异步块实现Send (Playground):

use std::collections::BTreeSet;
use std::future::ready;

pub fn test<T: Sync>(set: &BTreeSet<T>) -> impl Send + '_ {
    async move {
        for _ in set {
            ready(()).await;
        }
    }
}

但它给出了以下错误:

   Compiling playground v0.0.1 (/playground)
error[E0311]: the parameter type `T` may not live long enough
 --> src/lib.rs:4:44
  |
4 | pub fn test<T: Sync>(set: &BTreeSet<T>) -> impl Send + '_ {
  |             --                             ^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
  |             |
  |             help: consider adding an explicit lifetime bound...: `T: 'a +`

error: aborting due to previous error

error: could not compile `playground`

To learn more, run the command again with --verbose.

我根本不明白这个错误。添加生命周期界限并不能解决问题 (Playground),除非添加的生命周期界限是 'static (Playground)。

我尝试将 BTreeSet 替换为 VecVecDequeLinkedListHashSetBinaryHeap。全部编译没有错误。 BTreeSet 有什么特别之处?

1 个答案:

答案 0 :(得分:1)

该错误似乎是 Rust 中的一个错误 -- async 函数是相当新的,而且似乎存在许多奇怪或不正确的编译器错误的问题,尤其是泛型。我认为这可能是 issue #71058issue #64552

我发现像这样的生命周期错误经常发生,只是意味着编译器在说“救命!我很困惑。”

这是一个无偿更改的示例,我认为它在功能上是相同的:

use std::collections::BTreeSet;
use std::future::ready;

type ItemType = dyn Sync;

pub fn test<ItemType>(set: &BTreeSet<ItemType>) -> impl Send + '_ {
    async move {
        for _ in set {
            ready(()).await;
        }
    }
}

产生一个错误,我认为它更接近于导致编译器跳闸的错误(但仍然不正确)Playground

error: future cannot be sent between threads safely
 --> src/lib.rs:6:52
  |
6 | pub fn test<ItemType>(set: &BTreeSet<ItemType>) -> impl Send + '_ {
  |                                                    ^^^^^^^^^^^^^^ future created by async block is not `Send`
  |
note: captured value is not `Send`
 --> src/lib.rs:8:18
  |
8 |         for _ in set {
  |                  ^^^ has type `&BTreeSet<ItemType>` which is not `Send`
help: consider restricting type parameter `ItemType`
  |
6 | pub fn test<ItemType: std::marker::Sync>(set: &BTreeSet<ItemType>) -> impl Send + '_ {
  |                     ^^^^^^^^^^^^^^^^^^^

上述 Rust 错误表示未来不是 Send,如果异步闭包捕获了不支持 Send 的数据结构,就会出现这种情况。在这种情况下,它捕获确实支持 Send 的 BTreeSet。 为什么在 BTreeSet 而不是 Vec 或您提到的其他数据结构之一会发生这种情况,可能是其实现的语法存在一些微小的差异,这会导致编译器跳闸。

您创建了一个不错的最小示例,因此不确定您要完成什么。这里有一个可能有帮助的解决方法:

use std::collections::BTreeSet;
use std::future::ready;
use std::vec::Vec;
use futures::future::join_all;

pub async fn test<T: Sync>(set: &BTreeSet<T>) -> impl Send + '_ {
    let mut future_list = Vec::new();
    for _ in set {
        let new_future = async move {
            ready(()).await;
        };
        future_list.push(new_future);
    };
    join_all(future_list).await
}