为了使我的代码更易读,我将几个数据结构提取到一个单独的结构中:
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.
吗?通过选择其他设计可以避免这个问题吗?
版本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.y
和w.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
,会发生什么?您可以轻松地实现未定义的行为。