【问题标题】:How to mutate another item in a vector, but not the vector itself, while iterating over the vector?如何在迭代向量时改变向量中的另一个项目,而不是向量本身?
【发布时间】:2023-03-10 20:12:01
【问题描述】:

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

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

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&lt;RefCell&lt;...&gt;&gt; 包装向量。我认为这在运行时仍然会失败,因为 RefCell 会被迭代器借用,然后在循环体尝试借用它时会失败。
  2. 使用临时向量来累积未来的推送,并在循环结束后推送它们。这没关系,但需要比动态推送更多的分配。
  3. 不安全的代码,和指针乱七八糟。
  4. Iterator documentation 中列出的任何内容都无济于事。我查看了itertools,它似乎也无济于事。
  5. 使用while 循环和索引,而不是使用利用对外部向量的引用的迭代器。这没关系,但不允许我使用迭代器和适配器。我只想摆脱这个外循环并使用my_vec.foreach(...)

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

【问题讨论】:

  • some_index 是常量吗?还是可以预测?
  • 只看每次迭代时inner的具体内容。

标签: iterator rust borrow-checker


【解决方案1】:

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

请注意,如果您需要能够推送到inner 所指的向量,则此处的value 绑定很重要。 value 恰好是不包含引用的类型(它是 i32),因此它不会保持第一个借用活动(它在语句末尾结束)。然后,下一条语句可以可变地借用相同的向量或另一个向量,它会起作用。

如果我们改为写my_vec[some_index].borrow_mut().push(inner.borrow()[0]);,那么两个借用在语句结束之前都将处于活动状态。如果my_vec[some_index]inner 都引用同一个RefCell&lt;Vec&lt;i32&gt;&gt;,这将与RefCell&lt;T&gt; already mutably borrowed 发生恐慌。

【讨论】:

    【解决方案2】:

    在不改变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 关闭。

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

    【讨论】:

      猜你喜欢
      • 2017-08-29
      • 2017-12-29
      • 2016-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-20
      相关资源
      最近更新 更多