迭代遍历过滤器和继续条件的当量

问题描述 投票:0回答:1

为了使我的代码更易读,我将几个数据结构提取到一个单独的结构中:

struct S {
    x: Vec<i32>,
    y: HashSet<i32>,
    z: Vec<i32>,
}

仅存在于一个方法调用及其子调用中:

fn main() {
    let mut w = S { x: vec![], y: HashSet::new(), z: vec![], };
    do_part_of_the_work(&mut w);
}

fn do_part_of_the_work(w: &mut S) {
    // 1. Works
    for (index, &item) in w.x.iter().enumerate() {
        if w.y.contains(&item) {
            continue;
        }
        w.z[index] += 1;
    }

    // 2. Seems equivalent to 1. but doesn't work
    for (index, &item) in w.x.iter().enumerate()
        .filter(|&(_, &item)| !w.y.contains(&item)) {
        w.z[index] += 1;
    }

    // 3. Seems equivalent to 2. and doesn't work either
    for (index, &item) in w.iter_not_in_y() {
        w.z[index] += 1;
    }
}

impl S {
    fn iter_not_in_y(&self) -> impl Iterator<Item = (usize, &i32)> {
        self.x.iter().enumerate().filter(move |&(_, &item)| !self.y.contains(&item))
    }
}

我本质上是在尝试以代码块1.的形式执行代码块3.的工作,其中2.是无效的中间步骤,尽管这些似乎是等效的。如果S的所有属性都是局部变量,则似乎所有三个代码块都可以使用。

将代码块移到impl内也没有使我走得太远:

impl S {
    fn doing_it_inside_the_struct(&mut self) {
        // Doing 3. inside the struct instead, doesn't work either
        for (index, &item) in self.iter_not_in_y() {
            self.z[index] += 1;
        }
    }
}

为什么不阻止2.工作?它不等于1.吗?通过选择其他设计可以避免这个问题吗?

Playground

rust iterator closures immutability borrow-checker
1个回答
1
投票

版本2的问题:

for (index, &item) in w.x.iter().enumerate()
    .filter(|&(_, &item)| !w.y.contains(&item)) {
    w.z[index] += 1;
}

filter()的闭包通过引用捕获w,即,它包含&w。这意味着只要关闭有效,整个w就被借用。然后,当您尝试可变借入w.z时,编译器将失败。

第一个版本中的代码在分开的借位中使用w.yw.z,并且从不借用w本身,因此它可以工作。

解决方案是编写闭包以仅捕获w.y,而不捕获w。不幸的是,没有一个简单而又好的语法。我能写的更好的东西是:

for (index, &item) in w.x.iter().enumerate()
    .filter({
        let y = &w.y;
        move |&(_, &item)| !y.contains(&item)
     }) {
    w.z[index] += 1;
}

使用let y = &w.y;,您仅捕获y。现在,您必须将闭包标记为move,否则将捕获&y,并且将y用作暂时不起作用的临时对象。

版本3的问题类似:调用成员借用self,即&w,因此您以后无法对其进行修改。但是类似的解决方法将不起作用,因为如果您的iter_not_in_y()实现使用self.z,会发生什么?您可以轻松地实现未定义的行为。

© www.soinside.com 2019 - 2024. All rights reserved.