我正在浏览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
元组。
答案 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中提到了一些选项。
但原则很简单。确保某些内容(Vec
,Rc
,Gc
)涵盖所有权问题。当它被覆盖时,您可以像Ruby一样编程,因为所有权不再是问题。
答案 1 :(得分:1)
可能忽略了
Cell
结构的问题,完全支持像元组HashMap
这样的更直接的东西。
这是我通常在“带孔的二维网格”结构(生命游戏风格)中采用的方法。但是,在这种情况下,您发现您希望能够说明哪些单元格连接到其他单元格,因此您需要保留一堆布尔值(或位掩码或其他表示)。然后你会想要从一个单元格到另一个单元格进行遍历,然后意识到你不想再循环回来。
很快,您意识到自己是describing a graph。 As ArtemGr says:
当你创建一个指向自身的结构(循环图)时,所有权就会变得混乱。
最受欢迎的Rust图形库之一petgraph使用了所描述的“节点向量”。 From the documentation:
指数的利弊
图中的节点和边缘索引各自以紧凑间隔编号(n个节点从0到n - 1)的事实简化了 一些图算法。
您可以在图形大小之后选择图形索引整数类型。较小的尺寸可能具有更好的性能。
使用索引可以在遍历图表时进行变异,请参阅
Dfs
和.neighbors(a).detach()
。您可以使用等节点索引创建多个图形,但权重不同或边缘不同。
Graph
是一个常规的防锈集合,Send
和Sync
(只要关联数据N
和E
)。某些索引在节点或边缘移除期间发生移位,因此这是移除元素的缺点。指数不允许编译 时间检查作为参考。
我仍然使用这样的库,因为我相信作者更有可能花时间优化它而不是我。