I'm building a container for doing lazy initialization, and in order to allow the container to provide immutable references (i.e., to implement Deref), I need to use UnsafeCell so that I can still mutate the interior if the object needs to be initialized. I'm pretty sure that this is safe because UnsafeCell doesn't implement Sync, and so all of the immutable references to a given Lazy must be held by the same thread, but I'm still a bit unsure. Can anybody tell me if there's something I'm missing here wrt safety?
Ah, good point. However, I feel like reentrancy would never be safe with something like this because it would imply that your initialization routine depended on the thing it was initializing already being initialized, which is obviously impossible. So maybe something like RefCell might be better to catch those errors at runtime?
Simple enough indeed. So, is there a way to forbid this statically (ie via type system)?
I think from a safety perspective this is fine since it'll overflow the stack. Of course it's undesirable to allow such footgun in the first place. Ahh, the problem of accepting arbitrary callables from outside ...
@vitalyd It'll still technically trigger undefined behavior, because a mutable reference to the Lazy would be created while one already exists.
@joshlfRefCell could work, and would avoid the need for unsafe code. There are also various ways you could stick it in as a third enum variant, but be careful of undefined behavior.
So it looks like unfortunately RefCell won't actually work because borrow and borrow_mut return reference objects which live only for the lifetime of the borrow. I actually need the borrow to outlive the body of deref or deref_mut (the methods I'm implementing) and instead have the same lifetime as the Lazy object itself. The only way to accomplish that with RefCell would be to return these reference objects (Ref and RefMut) instead of references.