Thanks for the information! Let’s assume for now that we’re not worried about the unsafe code inside Sanakirja, and only the external interface.
I don’t understand your point about needing associated type constructors. I mean, I can see how they would make things neater, but I don’t see why they’re necessary. In particular, in the version the I posted, I “lifted” the lifetimes to the Representable trait, so I think that calling
db.get returning a value that borrows the
txn gives it the right lifetime. You can see examples of it at the bottom of the main function.
Anyway, I didn’t notice that my proposal allows multiple mutable references to the same database, so that’s definitely a huge soundness hole. Here are a couple of half-baked ideas to fix that:
As far as I can tell, the only issue is with largish values that are stored “on-page”, right? Small values pose no problem because we can just copy them instead of borrowing the memory, and large values stored on their own pages have stable locations for the whole duration of the transaction. So we could introduce a
StablyRepresentable trait that’s implemented for everything that has a stable location. A
Db would implement
StablyRepresentable if both
V do. If a
StablyRepresentable, then we can have multiple simultaneous mutable references to it, because nothing that it returns will ever be invalidated.
I think this scheme might already be enough for libpijul, because the only dbs-inside-a-db seem to be the ones in
branches, and those all store small values, so they could be stably representable.
I wonder if there’s a solution using the copy-on-write semantics together with “child” transactions. The idea is that you could have a
Txn and a
MutTxn going at the same time. You could read from the
Txn but not the
MutTxn, and you could write to the
MutTxn (without invalidating anything you’d read from the
Txn). Now, if at some point you want to read from the “fresh” data then you create a “child”
MutTxn, which has the effect of freezing the parent one. Then you can read from the parent while writing to the child.
For libpijul, this would be useful when applying patches, because first you update the branches and then you need to read the updated branches in order to update all the inodes.