I know that we are not talking about hot code, I am just thinking about the problem in general.
I focused mainly on the opt
fn, and I came up with two solutions... and I do not like any of them.
First solution
We can use the fact that Option
impl Default
:
pub fn opt(&self) -> Option<Rc<OptSettings>> {
let opt = self.opt.take();
let clone = opt.clone();
self.opt.set(opt);
clone
}
I hoped that the optimizer could be able to see that the content of the cell is written two times without being read and throw away some code, but unfortunately it is not the case. I cannot be sure, but I suspect that the runtime check in RefCell
is better in this case
Second solution
I am not completely sure this is sound (@kornel tell me what do you think), but in any case I don't like using unsafe
. Said that, we can deference the internal pointer and create a clone, which can be returned.
pub fn opt(&self) -> Option<Rc<OptSettings>> {
unsafe { &*self.opt.as_ptr() }.clone()
}
I tried to reason about soundedness in these terms: there is no function that returns a reference to the internal data, except for get_mut
, but this requires object mutability and it is not possible to have both mutable and const reference (from &self
). Therefore, it should be not possible to have an alias somewhere else, and the dereference operation is sound. ...right?
Last observations
I am thriving for Cell::update
, which is unstable at to date. Unfortunately, even update
won't be useful in this case with the actual proposal. The main problem with the update(&self, F) where F: FnMut(&mut T)
is related to the fact that it is possible to perform a call to update
inside the called lambda (because we are not taking a mutable reference to Cell
, obviously). In this way it is possible to easily trigger UB, and no one want that.
It looks like that many people really want a safe and sound way of working with Cell<T> where T: !Copy
, but it is not easy to come up with a valid abstraction