6

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

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

  • 内部可变性:给出Book一个字段,例如 price: RefCell<Option<i32>>初始化到RefCell::new(Option::None)何时Book初始化。稍后,当我们确定这本书的价格时,我们可以使用borrow_muttopriceSome(10)代替,然后我们就可以通过borrow它来检索它的价值。

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

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

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

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

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

4

2 回答 2

2

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

我想这种HashMap方法在这种情况下会很好,但是如果你想在其中拥有一些不是Copy你的价值的东西,你可能会遇到同样的问题,因为在某处可能存在对地图的出色引用。

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


编辑:

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

如果您处理的是Copy(例如i32)类型,则应考虑使用Cell而不是RefCell,因为它更轻。有关更详细的比较,请参阅https://stackoverflow.com/a/30276150/13679671

于 2020-09-19T10:41:43.043 回答
0

这里还有两种方法。

  1. Rc<RefCell<<Book>>在结构中随处使用price: Option<i32>>

  2. 声明 astrict BookId(usize)并制作 a library: HashMap<BookId, Book>。在您需要的任何地方通过它们进行所有参考BookId,从而间接参考书籍。

于 2020-09-19T21:05:27.143 回答