在嵌套迭代器中,按索引访问向量不起作用

In nested iterators accessing vector by index does not work

提问人:exocortex 提问时间:10/23/2023 更新时间:10/26/2023 访问量:76

问:

我有一个奇怪的行为,我无法向自己解释。如果想通过遍历两个 's 来创建新的字符串,但不知何故通过索引访问不起作用,因为我借用了东西。我的困惑来自这样一个事实,即索引不应该被复制,因为它是一个原始时间。Vec<String>

如果我直接遍历 's,该示例确实有效。编译器错误建议在第二个中使用并不能解决问题。Vec<String>movemap

这是我的例子:

fn f() -> Vec<String> {
    let num_outer = 2;
    let num_inner = 3;

    let outer_names = vec!["1", "2"];
    let inner_names = vec!["x", "y", "z"];

    (0..num_outer)
        .into_iter()
        .map(|outer_index| {
            (0..num_inner).into_iter().map(|inner_index| {
                format!(
                    "tuple ({},{})",
                    outer_names[outer_index], inner_names[inner_index],
                )
            })
        })
        .flatten()
        .collect::<Vec<String>>()
}

fn main() {
    let x = f();

    println!("{:?}", &x);
}
Rust 迭代器 闭包

评论


答:

2赞 JMAA 10/23/2023 #1

这是因为您需要闭包来捕获索引 by (或 by ,因为它们是类型),但也需要通过引用捕获 s。Rust 只允许你在语法上选择一种类型的捕获(如果你做了一个闭包,它就会通过引用来捕获所有内容)。moveCopyCopyVecmovemove

但是,手动操作很容易。您只需使用闭包和在闭包之前创建的“移入”引用。在您的情况下,这有效:move

fn f() -> Vec<String> {
    let num_outer = 2;
    let num_inner = 3;

    let outer_names = vec!["1", "2"];
    let inner_names = vec!["x", "y", "z"];

    let outer_names_ref = &outer_names;
    let inner_names_ref = &inner_names;
    (0..num_outer)
        .flat_map(|outer_index| {
            (0..num_inner).map(move |inner_index| {
                format!(
                    "tuple ({},{})",
                    outer_names_ref[outer_index], inner_names_ref[inner_index],
                )
            })
        })
        .collect::<Vec<String>>()
}

fn main() {
    let x = f();

    println!("{:?}", &x);
}

注意:我还删除了不必要的 s,因为范围文字已经是迭代器。我也改用了,因为 clippy 建议它(使用 clippy! 这很棒:))into_iterflat_map


通常,如果您需要闭包的“混合捕获”(即,某些变量的移动捕获,其他变量的引用捕获),这是执行此操作的方法。您使用闭包,但您显式创建了需要移入的引用,以便“通过引用”捕获内容。move

评论

0赞 cafce25 10/23/2023
默认设置不是通过引用捕获所有内容,而是通过引用的第一个,独占引用,与语义一起使用的移动。J.F. 确实移动了 xlet x = String::new(); let f = || x;
0赞 cafce25 10/23/2023
另外,作为提示,如果您在闭包之前捕获,则可以隐藏旧变量并避免考虑新名称,这是指定不应移动的内容的常用方法,或者例如应该共享的克隆。Arc
0赞 Kaplan 10/26/2023 #2

当然,同样的东西也可以用 2 个循环写得更短:

fn f() -> Vec<String> {
    let num_outer = 2;
    let num_inner = 3;
    let outer_names = ["1", "2"];
    let inner_names = ["x", "y", "z"];

    let mut rslt = vec![];
    for outer in outer_names.iter().take(num_outer) {
        for inner in inner_names.iter().take(num_inner) {
            rslt.push(format!(
                "tuple ({outer}, {inner})"
            ));
        }
    }
    rslt
}

操场

评论

0赞 exocortex 10/27/2023
是的,这也行得通。我用了一段时间的那个版本。我主要想了解在数组中使用诱导时出现此错误的原因。
0赞 Kaplan 10/27/2023
明白了。然而,在生产代码中,我更喜欢这种 2 循环解决方案,因为它的清晰度和更高的速度。
0赞 exocortex 10/28/2023
是的,当然!这只是我写的东西中发生的一个问题,它必须是这样的。我做了这个简单的例子,只是为了用更简单的方式说明这个问题。