I have a transaction system based on revertable operations. Each operation, in addition to its primary function, produces an UndoLog
that knows how to back out the change that was made:
trait UndoLog<T> {
fn revert(self, _:&mut T);
}
Semantically, this should be a consuming function: Once an operation has been reverted, attempting to revert it again is an error. I have also defined several combinators that can compose UndoLog
s:
-
()
is a no-op log -
(A,B)
revertsA
and thenB
-
Vec<T>
reverts eachT
in reverse order -
Option<T>
andEither<A,B>
revert whichever variant is present
The transaction object holds an exclusive reference to the object being modified and uses these combinators to maintain an overall undo log. Because the type of the undo log changes with every operation, the type of the transaction object itself also changes with every operation.
In most cases this is fine, but sometimes it would be useful to have a unified type for the UndoLog
s (and therefore the transactions that hold them). For example, it’s not feasible in my current setup to have a vector of transactions and apply an operation to just one of them.
To make this possible, I’d like to add a type-erasing combinator to the list above. It will obviously need to Box
the UndoLog
or otherwise store it on the heap. I can’t use Box<dyn UndoLog<T>>
, though, because UndoLog
isn’t object safe. I don’t want to change the function signature to self: Box<Self>
because that would force lots of unnecessary heap allocations.
There is some precedent for this: FnOnce
manages to be callable via a Box
despite taking an owned self
parameter, but I suspect it has special compiler support.