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

问题描述

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

到目前为止,我可以想到两种方法来实现此目的,但是它们都有缺点:

  • 内部可变性:给Book一个字段,例如price: RefCell<Option<i32>>,该字段在初始化RefCell::new(Option::None)时初始化为Book。稍后,当我们确定书的价格时,可以使用borrow_mutprice设置为Some(10),然后我们可以borrow对其进行取回。值。

    我的感觉是,总的来说,除非有必要,否则我想避免内部可变性,而且似乎在这里似乎不应该是所有必要的。由于需要使用Option,因此此技术也有些尴尬,因为价格要到以后才具有值(同时将其设置为0-1 ,但在我们从逻辑上可以确定价格已经被填满的地方,需要大量matchunwrap

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

    像我一样,从C的背景出发,HashMap在速度和内存上都感觉像是不必要的开销,对于已经有自然生存空间(在Book内部)和“应该”的数据通过简单的指针追逐即可访问。这种解决方案还意味着我必须使用一个附加参数(其中包含对price_table的引用)使许多功能杂乱无章。

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

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

解决方法

我认为您的第一种方法最适合这种情况。由于您对要写入的某些数据有出色的引用,因此必须在运行时检查借入规则,因此RefCell是可行的方法。 在RefCell内部,最好使用Optionenum之类的变体Price::NotSet或自定义Price::Set(i32)。如果您确实确定所有价格都已在某个时刻初始化,则可以编写方法price()来为您调用unwrap或在您的{{1} }包含RefCell

我猜想None的方法在这种情况下会很好,但是如果您希望其中的值不是HashMap,那么您可能会遇到相同的问题,因为在某处可能有很多关于地图的参考。

我同意Copy并不是惯用的方法,即使将HashMap作为值类型,仍然选择您的第一种方法。


编辑:

正如评论中指出的(谢谢!),对于这种情况有两个性能方面的考虑。首先,如果您真的知道,所包含的价格永远不会为零,则可以使用i32并免费获得std::num::NonZeroU16变体Option(请参阅{{ 3}})。

如果您要处理的是None(例如Copy)类型,则应该考虑使用i32而不是Cell,因为它更轻。有关更详细的比较,请参见documentation

,

还有两种方法。

  1. 在结构中使用Rc<RefCell<<Book>>的任何地方都使用price: Option<i32>>

  2. 声明一个strict BookId(usize)并创建一个library: HashMap<BookId,Book>。使您的所有参考文献BookId并因此在需要的地方通过它们间接地参考书籍。