如何使用HashMap创建结构,其中键与容器的类型相同

时间:2017-01-05 16:40:01

标签: rust

我正在浏览Mazes for Programmers并认为我会尝试在Rust中执行此操作(原始代码在Ruby中)。

本书中您要做的第一件事就是实现一个Cell类,其中包含指向其他单元格的链接。天真地,我以为我能够做到这样的事情:

pub struct Cell {
    row: u32,
    column: u32,

    links: HashMap<Box<Cell>, bool>,
}

这个想法是迷宫中的每个Cell都知道它连接到的其他单元格,而Maze只是Cells的集合。不幸的是,这导致一个人进入Rust的借用和所有权语义的双重链表问题,我不知道如何解决这个问题。

具体来说,我无法弄清楚如何在这里实施link

impl Cell {
    pub fn new (row: u32, column: u32) -> Cell {
        return Cell {
            row: row,
            column: column,
            links: HashMap::new(),
        };
    }

    pub fn link(&mut self, other: &mut Cell, bidi: bool) {
        self.links.insert(Box::new(other), true); // cannot get anything even remotely like this to work here.
    }
}

我看了一下关于选择你的保证的Rust Book的部分,它有关于指针包装和抽象的各自选择的一些很好的信息,但我真的不确定如何推理我想要的做编译器。

我认为我希望每个Cell结构(通过嵌入式HashMap)维护一个引用(在非显式使用该术语中)到其他Cells它是连接到,但由于该链接将是双向的,在我看来,我将最终使用Rc或类似的东西。

我也可能只是从书中Ruby代码的字面翻译中稍微退一步,并考虑一种更惯用的方法 - 可能忽略了Cell结构完全支持某些东西的问题更像是一个大的HashMap元组。

2 个答案:

答案 0 :(得分:2)

  

如何使用HashMap创建一个结构,其中键与容器的类型相同

     

原始代码在Ruby

我对Ruby并不熟悉,但我知道很多类似的语言,所以我会做一些有根据的猜测。

Ruby是thoroughly garbage collected language。这意味着你从不将某些类型的键存储在Ruby HashMap中。您可能认为您正在存储Cell类型的密钥,但实际发生的是您正在存储垃圾收集的指针Cell的实例。

指针只是索引进入某个内存区域。

现在,Rust是一种不同的语言。在Rust中,您可以将实际的Cell存储在HashMap的基础键数组中。换句话说,Rust为您提供了更多控制权,这通常可以转化为更好的内存使用和速度。

但是控制带来责任(除非你想破坏它们并快速打破它们)。在Rust中,您应该明确指定拥有实例的人。当你创建一个指向自身的结构(循环图)时,所有权就会变得混乱。

因此,实现循环结构的一种方法是将所有权concern移出等式!

在Ruby中,垃圾收集器解决了这个问题。内存管理是垃圾收集器的关注点,你可以实现循环结构,而无需处理它。

在Rust中你可以使用(实验性)垃圾收集器,也许是rust-gc来做同样的事情。

或者你可以亲自动手并实际管理记忆。

这并不像听起来那么难。您所需要的只是一些非循环结构拥有 Cell。 Vec<Cell>就足够了。将Cell存储在向量中后,您不必再担心所有权了。因为Cell现在由该向量拥有,简单明了。你可以创建循环结构,通过它的向量索引引用Cell,就像Ruby用它的指针一样!

这有各种各样的变化。当您管理所有权时,您就是老板,选择权归您所有。您可以使用memory pools(也称为竞技场)优化某些算法。您可以使用引用计数(那时不需要向量,但是您在从结构中删除单元格时要小心清除引用 - 以便打破引用计数周期)。您可以使用一种deque来分配内存,但没有向量执行的重新分配,然后将直接指针存储到该双端队列中。 this reddit discussion中提到了一些选项。

但原则很简单。确保某些内容(VecRcGc)涵盖所有权问题。当它被覆盖时,您可以像Ruby一样编程,因为所有权不再是问题。

答案 1 :(得分:1)

  

可能忽略了Cell结构的问题,完全支持像元组HashMap这样的更直接的东西。

这是我通常在“带孔的二维网格”结构(生命游戏风格)中采用的方法。但是,在这种情况下,您发现您希望能够说明哪些单元格连接到其他单元格,因此您需要保留一堆布尔值(或位掩码或其他表示)。然后你会想要从一个单元格到另一个单元格进行遍历,然后意识到你不想再循环回来。

很快,您意识到自己是describing a graphAs ArtemGr says

  

当你创建一个指向自身的结构(循环图)时,所有权就会变得混乱。

最受欢迎的Rust图形库之一petgraph使用了所描述的“节点向量”。 From the documentation

  

指数的利弊

     
      
  • 图中的节点和边缘索引各自以紧凑间隔编号(n个节点从0到n - 1)的事实简化了   一些图算法。

  •   
  • 您可以在图形大小之后选择图形索引整数类型。较小的尺寸可能具有更好的性能。

  •   
  • 使用索引可以在遍历图表时进行变异,请参阅Dfs.neighbors(a).detach()

  •   
  • 您可以使用等节点索引创建多个图形,但权重不同或边缘不同。

  •   
  • Graph是一个常规的防锈集合,SendSync(只要关联数据NE)。

  •   
  • 某些索引在节点或边缘移除期间发生移位,因此这是移除元素的缺点。指数不允许编译   时间检查作为参考。

  •   

我仍然使用这样的库,因为我相信作者更有可能花时间优化它而不是我。