如何在可变元素的另一个可变迭代中迭代相同元素?

5
我有一个由Element组成的数组,我想迭代它来做一些事情,然后在循环中迭代所有Element来做某些事情。这些元素之间存在关联,因此我想迭代到所有其他元素以检查某些内容。这些元素是可变引用,出于某种原因。虽然有点笼统,但我试图保持通用(也许不应该)。
struct Element;

impl Element {
    fn do_something(&self, _e: &Element) {}
}

fn main() {
    let mut elements = [Element, Element, Element, Element];

    for e in &mut elements {
        // Do stuff...

        for f in &mut elements {
            e.do_something(f);
        }
    }
}

正如预期的那样,我收到了这个错误:

error[E0499]: cannot borrow `elements` as mutable more than once at a time
  --> src/main.rs:13:18
   |
10 |     for e in &mut elements {
   |              -------------
   |              |
   |              first mutable borrow occurs here
   |              first borrow later used here
...
13 |         for f in &mut elements {
   |                  ^^^^^^^^^^^^^ second mutable borrow occurs here

我知道这在Rust中是正常行为,但避免这个错误的推荐方法是什么?我应该先复制元素吗?放弃使用循环并以不同的方式进行迭代?学习代码设计?
有没有一种Rust的方法来做到这一点?

你不能使用迭代器来完成这个操作。我会建议使用基于索引的迭代方式(for i in 0..elements.len())。 - oli_obk
2个回答

8

您可以使用索引迭代而不是迭代器进行迭代。然后,在内部循环中,您可以使用split_at_mut来获得对同一切片的两个可变引用。

for i in 0..elements.len() {
    for j in 0..elements.len() {
        let (e, f) = if i < j {
            // `i` is in the left half
            let (left, right) = elements.split_at_mut(j);
            (&mut left[i], &mut right[0])
        } else if i == j {
            // cannot obtain two mutable references to the
            // same element
            continue;
        } else {
            // `i` is in the right half
            let (left, right) = elements.split_at_mut(i);
            (&mut right[0], &mut left[j])
        };
        e.do_something(f);
    }
}

2
请注意,这个解决方案和原始文章中的代码略有不同,因为doSomething永远不会使用相同的两个值进行调用。另一个回答解释了原因。 - Shepmaster

4

你不能这样做,就这样。引用规则规定,重点在于:

在任何时候,您可以拥有一个可变引用或任意数量的不可变引用

在第一次迭代中,您试图获取数组中第一个元素的两个可变引用。这是不允许的。


你的方法完全不需要可变引用 (fn do_something(&self, e: &Element) {}),因此最简单的方法就是切换到不可变迭代器:
for e in &elements {
    for f in &elements {
        e.doSomething(f);
    }
}

如果您确实需要在循环内执行突变操作,您还需要切换到 内部可变性。这将使规则的执行从编译时转移到运行时,因此当您尝试同时获取两个可变引用指向同一项时,您现在会得到一个恐慌:

use std::cell::RefCell;

struct Element;

impl Element {
    fn do_something(&mut self, _e: &mut Element) {}
}

fn main() {
    let mut elements = [
        RefCell::new(Element),
        RefCell::new(Element),
        RefCell::new(Element),
        RefCell::new(Element),
    ];

    for e in &elements {
        for f in &elements {
            // Note that this will panic as both `e` and `f` 
            // are the same value to start with
            let mut e = e.borrow_mut();
            let mut f = f.borrow_mut();
            e.do_something(&mut f);
        }
    }
}

你的方法根本不需要可变引用。如果实际上是 fn doSomething(&mut self, e: &mut Element) {} 并且同时改变了 e 和 f,那该怎么办? - Tuupertunut
@Tuupertunut,这就是“你不能这样做”的意思,正如关于引用规则的部分所述。Rust不允许对同一项使用两个可变引用。 - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接