在迭代向量时如何改变向量中的另一个项而不是向量本身?

时间:2016-03-06 04:14:14

标签: iterator rust borrow-checker

我很清楚迭代向量不应该让循环体任意地改变向量。这可以防止迭代器失效,容易出错。

但是,并非所有类型的突变都会导致迭代器失效。请参阅以下示例:

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 documentation中列出的任何内容都无济于事。我检查了itertools,看起来它也没有帮助。
  5. 使用while循环和索引而不是使用迭代器来使用对外部向量的引用。这没关系,但不允许我使用迭代器和适配器。我只是想摆脱这个外循环并使用my_vec.foreach(...)
  6. 是否有任何成语或任何图书馆可以让我做得很好不安全的功能可以,只要他们不向我公开指示。

2 个答案:

答案 0 :(得分: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造成恐慌。

答案 1 :(得分: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关闭。

这是安全的,相对容易的,而且非常灵活。但是,它不适用于迭代器适配器。