数据待定:内部可变性还是单独的HashMap?

Nat*_*dge 6 hashmap rust interior-mutability

我有一个struct,称之为Book,比方说存储书店出售的一本书的数据。它需要在某些数据结构中的许多地方被引用(例如 with Rc),因此不能以正常方式可变地借用。然而,它有一些属性,比如它的价格,需要在初始化之后的某个时间填写,在对象已经有未完成的引用之后。

到目前为止,我可以想到两种方法来做到这一点,但它们都有缺点:

  • 内部可变性:给出Book一个字段,例如 price: RefCell<Option<i32>>which 被初始化为RefCell::new(Option::None)whenBook被初始化。稍后,当我们确定书的价格时,我们可以使用borrow_mutto set pricetoSome(10)代替,然后我们可以使用borrow它来检索它的价值。

    我的感觉是,一般来说,除非有必要,否则人们希望避免内部可变性,而且这里似乎没有那么必要。这种技术也有点尴尬,因为Option我们需要它,因为价格直到稍后才会有值(并且将其设置为0-1同时看起来不像 Rustlike),但是它需要很多地方matches 或unwraps我们可以从逻辑上确定价格已经被填写。

  • 单独的表:根本不将价格存储在里面Book,而是创建一个单独的数据结构来存储它,例如price_table: HashMap<Rc<Book>, i32>. 有一个函数,它在价格确定时创建并填充该表,然后通过引用(可变或不可变)将其传递给需要知道或更改书籍价格的每个函数。

    像我一样来自 C 背景,HashMap感觉在速度和内存方面都是不必要的开销,因为数据已经有一个自然的地方(内部Book)并且“应该”通过一个简单的指针追逐来访问。这个解决方案还意味着我必须用一个引用price_table.

这两种方法中的一种在 Rust 中通常更惯用,还是有其他方法可以避免这种困境?我确实看到了Once,但我认为这不是我想要的,因为我仍然需要在初始化时知道​​如何填写price,而我不知道。

当然,在其他应用程序中,我们可能需要一些其他类型而不是i32表示我们想要的属性,所以我希望能够处理一般情况。

Nik*_*rin 2

我认为您的第一种方法最适合这种情况。由于您对要写入的某些数据有未完成的引用,因此您必须在运行时检查借用规则,这RefCell就是要走的路。在 中RefCell,更喜欢 anOptionenum具有变体的自定义,如Price::NotSetPrice::Set(i32)。如果您确实确定所有价格都在某个时刻初始化,您可以编写一个方法price()来调用unwrap您或在您RefCell包含None.

我想这种HashMap方法对于这种情况来说是没问题的,但是如果你想要有一些不符合Copy你的价值的东西,你可能会遇到同样的问题,因为在地图的某个地方可能有未完成的引用。

我同意,即使作为值类型,这HashMap也不是这里的惯用方式,并且仍然选择您的第一种方法。i32


编辑:

正如评论中指出的(谢谢!),这种情况有两个性能考虑因素。首先,如果您确实知道所包含的价格永远不会为零,您可以免费使用std::num::NonZeroU16并获取该Option变体(请参阅文档)。None

如果您正在处理的类型是Copy(例如i32),您应该考虑使用Cell而不是RefCell,因为它更轻。更详细的比较请参见/sf/answers/2119330531/

  • 另外:如果您的书店*从不*免费赠送书籍,您可以使用`Option&lt;std::num::NonZeroU64&gt;`而不是`Option&lt;u64&gt;`。这将具有与“u64”相同的内存布局,从而使“Option”自由。 (2认同)