【问题标题】:Efficiently mutate a vector while also iterating over the same vector在对同一向量进行迭代的同时有效地改变向量
【发布时间】:2018-08-15 01:47:15
【问题描述】:

我有一个结构向量,我将向量中的每个元素与其他所有元素进行比较,并且在某些情况下会改变当前元素。

我的问题是你不能同时发生可变和不可变借用,但我不确定如何在不克隆当前元素或整个向量的情况下重新构建我的问题以解决这个问题,这似乎是一种浪费,因为我只对当前元素进行了变异,并且不需要将其与自身进行比较(我跳过了这种情况)。

我确信在 Rust 中有一种惯用的方式来做到这一点。

struct MyStruct {
    a: i32,
}

fn main() {
    let mut v = vec![MyStruct { a: 1 }, MyStruct { a: 2 }, MyStruct { a: 3 }];

    for elem in v.iter_mut() {
        for other_elem in v.iter() {
            if other_elem.a > elem.a {
                elem.a += 1;
            }
        }
    }
}

【问题讨论】:

  • 了解您当前如何尝试构建代码可能会有所帮助。这似乎非常可行。
  • 我添加了一个例子。我偶然发现了this quesion,这让我认为 split_at_mut 或类似的东西可能是解决方案。
  • 您基本上是在尝试访问切片的所有对。奇怪的是,我发现没有板条箱可以做到这一点。
  • @Boiethios itertools::iproduct 会这样做...当然不会解决可变性问题。
  • @kazemakase 仅供参考,我为此做了一个迭代器:gitlab.com/snippets/1702560 也许我会为这样的工具创建一个。

标签: rust


【解决方案1】:

如果可以修改v的type类型,并且v的元素为Copy,则可以将MyStruct包裹在Cell中。

#[derive(Copy, Clone)] 
struct MyStruct {
    a: i32,
}

fn main() {
    use std::cell::Cell;
    let v = vec![
        Cell::new(MyStruct { a: 1 }),
        Cell::new(MyStruct { a: 2 }),
        Cell::new(MyStruct { a: 3 }),
    ];

    for elem in v.iter() {
        for other_elem in v.iter() {
            let mut e = elem.get();
            if other_elem.get().a > e.a {
                e.a += 1;
                elem.set(e);
            }
        }
    }
}

如果您将 &mut 传递给切片(或可以转换为切片的 &mut),请使用 Cell::from_mutCell::as_slice_of_cells 并使用与上述相同的技巧(假设切片的元素是 @ 987654327@).

【讨论】:

    【解决方案2】:

    最简单的方法是只使用索引,它不涉及任何长期借用:

    for i in 0..v.len() {
        for j in 0..v.len() {
            if i == j { continue; }
            if v[j].a > v[i].a {
                v[i].a += 1;
            }
        }
    }
    

    如果你真的,真的想要使用迭代器,你可以通过将 Vec 分成不相交的切片来实现:

    fn process(elem: &mut MyStruct, other: &MyStruct) {
        if other.a > elem.a {
            elem.a += 1;
        }
    }
    
    for i in 0..v.len() {
        let (left, mid_right) = v.split_at_mut(i);
        let (mid, right) = mid_right.split_at_mut(1);
        let elem = &mut mid[0];
    
        for other in left {
            process(elem, other);
        }
        for other in right {
            process(elem, other);
        }
    }
    

    【讨论】:

    • 完美,谢谢!回想起来,使用索引似乎很明显。
    • 任何可以改变向量大小的东西都可能使引用无效,所以我可以理解为什么向量容器本身在迭代时不能被变异,但是 Rust 试图通过阻止我来拯救我从改变元素?
    • @JosephGarvin 你可以改变元素,但你不能同时拥有一个共享引用作为一个可变引用。考虑您的向量 (v1) 是否包含向量 (v2),并且您同时使用 iter() 和 iter_mut() 对 v1 进行迭代。您将拥有 v2 的 &mut 和 &。现在考虑通过 & 得到 v2 的第一个元素,然后通过 &mut 将 vec 的大小调整为 0。第一个元素的 & 现在指向无效内存。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-28
    • 2017-08-29
    • 2023-03-10
    相关资源
    最近更新 更多