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(...).

是否有任何习语或任何库可以让我很好地做到这一点 不安全的函数只要不暴露指向我的指针就可以了。

4

2 回答 2

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]);
}

注意:请注意,中的索引secondindex.

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

于 2016-03-06T14:26:13.017 回答
3

您可以将每个内部向量包装在 a 中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

于 2016-03-06T05:24:26.910 回答