在嵌套迭代器中指定生命周期以进行展平

Specifying lifetimes in nested iterators for flattening

我正在编写一个函数,它应该采用几个向量并将它们的笛卡尔积(它们的所有对组合)作为行优先顺序的向量。换句话说,如果我有

let x_coords = vec![1, 2, 3];
let y_coords = vec![4, 5, 6];

我要生产

vec![ [1,4], [1,5], [1,6], [2,4], [2,5], [2,6], [3,4], [3,5], [3,6] ]

这对 .flat_map() 来说似乎是一份完美的工作:

fn main() {
    let x_coords = vec![1, 2, 3];
    let y_coords = vec![4, 5, 6];

    let coord_vec: Vec<[isize; 2]> =
        x_coords.iter().map(|&x_coord| {
            y_coords.iter().map(|&y_coord| {
                [x_coord, y_coord]
            })
        }).flat_map(|s| s).collect();

    // Expecting to get: vec![ [1,4], [1,5], [1,6], [2,4], [2,5], [2,6], [3,4], [3,5], [3,6] ]
    println!("{:?}", &coord_vec);
}

但这行不通,因为&x_coord活的时间不够长。根据编译器的说法,它最终进入 y_coords 映射,然后再也不会退出。

我尝试在闭包中使用 .clone()move,但编译器以多行 Note: 行的形式进行了冗长且不清楚的讲授。

我是完全偏离了 flat_map 的基础,还是可以保存它?

解决方案

你们真的很亲密!这有效:

let coord_vec: Vec<_> = x_coords.iter()
    .flat_map(|&x_coord| {
        y_coords.iter().map(move |&y_coord| {
        //                  ^^^^
            [x_coord, y_coord]
        })
    })
    .collect();

我唯一添加的是内部闭包前面的 move 关键字。为什么?下面让我们试着理解编译器在想什么!

一些注意事项:

  • 我将 map 重命名为 flat_map 并删除了第二个 flat_map 调用...你让你的生活变得更加复杂 ;-)
  • 我省略了coord_vec的部分类型注解,因为没必要

说明

类型 x_coordi32(或任何其他整数)。不是参考或任何东西,而是直接的价值。这意味着 x_coord 属于封闭函数,它恰好是一个闭包,特别是 "outer" 闭包,您传递给 flat_map。因此 x_coord 只存在于闭包内,而不是更长。这就是编译器告诉你的。到目前为止一切顺利。

当您定义第二个闭包("inner" 闭包)时,您可以访问环境,特别是 x_coord。现在的重要问题是:闭包如何访问它的环境?它可以通过不可变引用、可变引用和值来实现。 Rust 编译器确定需要什么样的环境访问权限,并选择有效的 "least intrusive" 选项。让我们看看你的代码:编译器发现闭包只需要不变地借用环境(因为 i32Copy 因此闭包可以将其 &i32 转换为 i32 轻松)。

但在这种情况下,编译器的推理是错误的!借用其环境的闭包导致有限的生命周期。在这种情况下,我们需要闭包比它能活得更久,因此会出现错误。

通过添加 move,我们强制编译器按值将环境传递给闭包(转移所有权)。这样闭包就不会借用任何东西并且可以永远存在(满足'static)。