I have a struct
, call it Book
, which let's say stores data on a book sold by a bookstore. It needs to be referenced at many places in some data structure (e.g. with Rc
) and so cannot be borrowed mutably in the normal way. However, it has some attribute, say its price, that needs to be filled in at some time later than initialization, after the object already has outstanding references.
So far I can think of two ways to do this, but they both have disadvantages:
Interior mutability: give
Book
a field such asprice: RefCell<Option<i32>>
which is initialized toRefCell::new(Option::None)
whenBook
is initialized. Later on, when we determine the price of the book, we can useborrow_mut
to setprice
toSome(10)
instead, and from then on we canborrow
it to retrieve its value.My sense is that in general, one wants to avoid interior mutability unless necessary, and it doesn't seem here like it ought to be all that necessary. This technique is also a little awkward because of the
Option
, which we need because the price won't have a value until later (and setting it to0
or-1
in the meantime seems un-Rustlike), but which requires lots ofmatch
es orunwrap
s in places where we may be logically certain that the price will have already been filled in.Separate table: don't store the price inside
Book
at all, but make a separate data structure to store it, e.g.price_table: HashMap<Rc<Book>, i32>
. Have a function which creates and populates this table when prices are determined, and then pass it around by reference (mutably or not) to every function that needs to know or change the prices of books.Coming from a C background as I do, the
HashMap
feels like unnecessary overhead both in speed and memory, for data that already has a natural place to live (insideBook
) and "should" be accessible via a simple pointer chase. This solution also means I have to clutter up lots of functions with an additional argument that's a reference toprice_table
.
Is one of these two methods generally more idiomatic in Rust, or are there other approaches that avoid the dilemma? I did see Once
, but I don't think it's what I want, because I'd still have to know at initialization time how to fill in price
, and I don't know that.
Of course, in other applications, we may need some other type than i32
to represent our desired attribute, so I'd like to be able to handle the general case.