【问题标题】:Modify a struct while iterating over an Iterable in the struct [duplicate]在迭代结构中的Iterable时修改结构[重复]
【发布时间】:2019-08-12 10:13:10
【问题描述】:

我正在 Rust 中实现滚动初筛。结构如下所示:

struct Sieve {
    window_index: u32,
    window_size: u32,
    board: BitVec,
    primes: Vec<u32>,
}

有一个移动的“窗口”,其中在大小为window_size 的板上标记了质数倍数,遇到的任何未标记的数字都被称为质数。

当窗口移动时,新板需要重新初始化,并标记之前发现的所有素数的倍数。

let square_optimization = (self.window_index + self.window_size).integer_sqrt();
for &prime in self.primes.iter().filter(|&&p| p < square_optimization) {
    let remainder = self.window_index % prime;
    let start_prime = if remainder > 0 { self.window_index + prime - remainder } else { remainder };
    if start_prime > self.window_index + self.window_size { return; }

    for i in (start_prime % self.window_size..self.window_size).step_by(prime as usize) {
        self.board.set(i as usize, true)
    }
}

确切的逻辑无关紧要,但是您可以看到self.primes 的不可变借用和self.board 的可变借用。编译器很高兴。将逻辑提取到函数中时出现问题:

fn mark_multiples(&mut self, prime: u32) {
    let remainder = self.window_index % prime;
    let start_prime = if remainder > 0 { self.window_index + prime - remainder } else { remainder };
    if start_prime > self.window_index + self.window_size { return; }

    for i in (start_prime % self.window_size..self.window_size).step_by(prime as usize) {
        self.board.set(i as usize, true)
    }
}

fn compute_chunk(&mut self) {
    let square_optimization = (self.window_index + self.window_size).integer_sqrt();
    for &prime in self.primes.iter().filter(|&&p| p < square_optimization) {
        self.mark_multiples(prime)
    }

    // do work
}
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/lib.rs:54:13
   |
53 |         for &prime in self.primes.iter().filter(|&&p| p < square_optimization) {
   |                       --------------------------------------------------------
   |                       |
   |                       immutable borrow occurs here
   |                       immutable borrow later used here
54 |             self.mark_multiples(prime)
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

为什么借用检查器会在这里感到困惑? self 在第一种情况下不是可变借用的吗?

【问题讨论】:

  • 你确定是借阅检查员弄糊涂了吗? ;)
  • 我还没有遇到过一种语言,其中方法提取不能“正常工作”,但对 rust 来说是新手,不,我不确定。
  • mark_multiples 可变地借用了整个self,但prime 也是不可变地借用 self。如果mark_multiples 的实现决定替换self 并删除原始版本,prime 会发生什么?现在,知道你的函数不会这样做,但编译器不会!
  • Rust 引用就像读写锁一样工作。您要么以可变方式独占获取 (&amp;mut),要么以不可变方式获取共享锁 (&amp;)。独占和共享不能同时发生。
  • @arlyon,方法提取在 Rust 中确实不是“正常工作”,因为借用检查器在每个函数中单独工作,只考虑其接口的签名。所以你可以提取函数,但是如果你传递给它们的引用比它们实际接触的更多,它们可能会在混合代码没有的地方开始冲突。这是非常有意的,因为它允许您重构函数体,确信您不会与某些调用者中的代码产生冲突。

标签: rust


【解决方案1】:

为什么?因为self.primes 借用了不可变的(因为Vec::iter,即新创建的迭代器,Iter 正在引用由self 拥有的向量)并且当这种情况发生时mark_multiples 尝试将self 借用为可变的.借用规则简单明了:

  1. 您可以在给定时间对对象进行任意数量的不可变借用,或者
  2. 您可以在给定时间对一个对象进行一次可变借用。

因此,你不能有一个可变借用 self,而你仍然持有它的不可变借用,这正是编译器拒绝编译你的代码的原因。

为什么这个安全功能在这里被认为有好处?如果您想要的内容对您来说是允许的,那么您可能会在方法self.mark_multiples 中更改self.primes 的值,同时在self.compute_chunk 中对其进行迭代。您很容易遇到引用无效内存位置的问题!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 2017-12-10
    • 2017-07-27
    • 2011-05-29
    • 2020-05-23
    • 1970-01-01
    相关资源
    最近更新 更多