如何更新BTreeSet中的所有值?

时间:2019-02-14 18:53:18

标签: collections module rust

我有一个集合,它是某个模块中结构中的一个字段。我想从另一个模块更新集合中的所有值。

我写了一些代码来模仿我想要实现的目标。它缩短了一点,但是我认为它包含了所有需要的部分。这段代码中没有保存集合的结构,但是可以想象这是一个返回集合的getter。我在评论中添加了我认为的外观。

pub mod pos {
    use std::cmp::{Ordering, PartialEq};

    #[derive(PartialOrd, PartialEq, Eq, Hash, Debug, Copy, Clone)]
    pub struct Pos {
        pub x: i32,
        pub y: i32,
    }

    #[allow(dead_code)]
    impl Pos {
        pub fn of(x: i32, y: i32) -> Self {
            Self { x, y }
        }

        pub fn offset(&mut self, pos: &Self) -> Self {
            self.x += pos.x;
            self.y += pos.y;

            *self
        }
    }

    impl Ord for Pos {
        fn cmp(&self, other: &Self) -> Ordering {
            if self.x < other.x {
                Ordering::Less
            } else if self.eq(other) {
                Ordering::Equal
            } else {
                Ordering::Greater
            }
        }
    }
}

mod test {
    use crate::pos::Pos;
    use std::collections::BTreeSet;

    #[test]
    fn test_iterators() {
        let mut data_in_some_strct: BTreeSet<Pos> = BTreeSet::new();

        data_in_some_strct.insert(Pos::of(1, 1));
        data_in_some_strct.insert(Pos::of(2, 2));
        data_in_some_strct.insert(Pos::of(3, 3));
        data_in_some_strct.insert(Pos::of(4, 4));

        // mimic getter call ( get_data(&mut self) -> &BTreeSet<Pos> {...}
        //    let set = data_in_some_strct;   // works, but not a reference
        let set = &data_in_some_strct; // doesn't work, How to adjust code to make it work??

        data_in_some_strct = set
            .into_iter()
            .map(|mut p| p.offset(&Pos::of(1, 0)))
            .inspect(|p| println!("{:?}", *p))
            .collect();

        assert_eq!(data_in_some_strct.contains(&Pos::of(2, 1)), true);
        assert_eq!(data_in_some_strct.contains(&Pos::of(3, 2)), true);
        assert_eq!(data_in_some_strct.contains(&Pos::of(4, 3)), true);
        assert_eq!(data_in_some_strct.contains(&Pos::of(5, 4)), true);
    }
}

Playground

error[E0596]: cannot borrow `*p` as mutable, as it is behind a `&` reference
  --> src/lib.rs:56:26
   |
56 |             .map(|mut p| p.offset(&Pos::of(1, 0)))
   |                       -  ^ `p` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |                       |
   |                       help: consider changing this to be a mutable reference: `&mut pos::Pos`

我设法不借钱就能使它工作,但是我想借钱使它能工作。我想还有更多的方法可以实现它。欢迎发表评论以帮助我的Rust brain dendrites进行连接。

2 个答案:

答案 0 :(得分:2)

BTreeSet未实现impl<'a, T> IntoIterator for &'a mut BTreeSet<T>(这会破坏树)。

您只能使用IntoIteratorexample之类的通过mut实现impl<'a, T> IntoIterator for &'a mut Vec<T>的类型来做到这一点。

答案 1 :(得分:2)

您无法对HashSetBTreeSet中的项目进行突变,因为这些项目的 value 决定了它们的存储和访问方式。如果对它们进行突变,则为Stargateur mentioned,则会破坏集合的机制。在HashSet的情况下,您将更改项目的哈希值,从而确定数据的存储位置。在BTreeSet的情况下,该算法基于项目的排序方式。

您可以通过拥有所有权来完成此任务,因为您可以使用原始集并生成新的格式良好的集。您不能拥有借入值的所有权,因为那样会留下一个悬空的指针,Rust不会让您这样做。

一种可能的解决方案是用空的临时替换原始的设备。然后,您可以像在工作代码中一样拥有其内容的所有权,最后将新更新的集写在原始集上:

let set = std::mem::replace(&mut data_in_some_strct, BTreeSet::new());

data_in_some_strct = set.into_iter()
    .map(|mut p| p.offset(&Pos::of(1,0)))
    .inspect(|p| println!("{:?}", *p))
    .collect();