如何在迭代向量时改变另一个项目,但不改变向量本身?

4

我认为,对于一个向量进行迭代时,循环体不应该随意改变向量。这可以防止迭代器失效,从而避免出现错误。

然而,并非所有类型的改变都会导致迭代器失效。请看以下示例:

let mut my_vec: Vec<Vec<i32>> = vec![vec![1,2], vec![3,4], vec![5,6]];
for inner in my_vec.iter_mut() {        // <- or .iter()
    // ...
    my_vec[some_index].push(inner[0]);  // <-- ERROR
}

这样的变异不会使my_vec的迭代器无效,但是它是被禁止的。它可能会使my_vec[some_index]中特定元素的任何引用无效,但我们不使用任何这样的引用。
我知道这些问题很常见,我不需要解释。我正在寻找一种重构方式,以便我可以摆脱这个循环。在我的实际代码中,我有一个庞大的循环体,除非我漂亮地表达这一点,否则我无法模块化它。
到目前为止,我想到了以下几点:
1.用Rc<RefCell<...>>包装向量。我认为这仍然会在运行时失败,因为RefCell将被迭代器借用,当循环体尝试借用它时,它将失败。
2.使用临时向量累积未来的推送,并在循环结束后推送它们。这还不错,但需要比即时推送更多的分配。
3.不安全的代码和指针操作。
4.Iterator文档中列出的任何内容都没有帮助。我查看了itertools,看起来它也没有帮助。
5.使用while循环和索引,而不是使用迭代器利用对外部向量的引用。这还不错,但不能让我使用迭代器和适配器。我只想摆脱这个外部循环并使用my_vec.foreach(...)
是否有任何习惯用法或库可以让我漂亮地做到这一点?不安全的函数可以,只要它们不向我公开指针。

some_index是常量吗?还是可以预测的? - Matthieu M.
它完全取决于每次迭代中inner的具体内容。 - loudandclear
2个回答

3

您可以将每个内部向量包装在RefCell中。

use std::cell::RefCell;

fn main() {
    let my_vec : Vec<RefCell<Vec<i32>>> = vec![
        RefCell::new(vec![1,2]),
        RefCell::new(vec![3,4]),
        RefCell::new(vec![5,6])];
    for inner in my_vec.iter() {
        // ...
        let value = inner.borrow()[0];
        my_vec[some_index].borrow_mut().push(value);
    }
}

请注意,在这里绑定的value是很重要的,如果您需要能够向inner所引用的向量推送数据。value碰巧是不包含引用的类型(即i32),因此它不会保持第一个借用处于活动状态(它在语句结束时结束)。然后,下一条语句可能会可变地借用相同的向量或另一个向量,并且它将起作用。
如果我们改写成my_vec[some_index].borrow_mut().push(inner.borrow()[0]);,则两个借用将一直保持活动状态,直到语句结束。如果my_vec[some_index]inner都引用同一个RefCell<Vec<i32>>,则会出现RefCell<T> already mutably borrowed的崩溃情况。

3

不改变 my_vec 的类型,你可以通过索引访问和使用 split_at_mut 来实现:

for index in 0..my_vec.len() {
    let (first, second) = my_vec.split_at_mut(index);

    first[some_index].push(second[0]);
}

注意:请小心,second 中的索引偏移 index

这种方法是安全、相对简单和非常灵活的。然而,它无法与迭代器适配器一起使用。


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