Since RefCell<T> allows for interior mutability, then obtaining a mut &T using RefCell::borrow_mut(), we can do a move via mem::replace(), and that too in (apparently) safe code; whereas documentation for Pin<T> states, that it isn't possible for safe code to obtain a reference to &mut T.
Intutively, it appears that it violates the pin gurantee; or, am I wrong here?
In Pin<Rc<RefCell<T>>> the pinned value is the RefCell<T>, not the T, so the pin guarantee doesn't apply to it. It would apply if RefCell<T> considered the T it contains to be structurally pinned, but it does not because that would be unsound.
I had once written down concretely what a Mutex (same idea as RefCell, of course) API surface could look like if it wanted to actually consider its contained value structurally pinned. This is the API I could come up with, if you’re interested
It’s a rare use-case where a Pin<&T>-type actually ends up being useful. I also added an API method that can lock with &Self instead of Pin<&Self>-access to the Mutex, but it will notgive you mutable access to the value (unless it’s Unpin), only immutable access.
I understand, that techincally it would not violate the Pin<T> gurantee. But since semantically it appears to do, then why exactly would it be unsound to consider T structurally pinned?
It would be unsound for RefCell<T> to structurally pin the T, because given an Pin<&mut RefCell<T>> you can obtain an &RefCell<T>, which lets you obtain &mut T. Safe APIs must not hand out bare mutable references to pinned values, so pinning a RefCell<T> must not pin the T.
To you it appears to violate the pinning guarantee because you're intuitively assuming the T to be structurally pinned, but it is not. If T was indeed structurally pinned it would be unsound as you expect
One could make a separate “only-pinning RefCell” type, right? The unsound part is that the RefCell can't reliably tell whether it is pinned and thus whether its contents should be taken as pinned too.
If I understand correctly, RefCell<T> itself makes no gurantee about pinning at all, simply because it's not aware of the address sensitivity of T, right? The following is my formulation.
Suppose T becomes address-sensitive for a part of its lifecycle, there's no way to notify RefCell<T> so as to forbid safely obtaining a &mut T, therefore it ignores its pinned state altogether, because RefCell<T>'s original goal is to allow dynamic borrow checking, not tracking whether it's inner value is pinned or not.
I believe this would be solved by having borrow_mut take Pin<&Self>
No, the key point is that you cannot have a pinned T in a RefCell<T> in the first place. RefCell can't ignore a pinned state if that didn't exist in the first place
Well, by saying "ignored", I actually meant RefCell<T> isn't aware whether a pinned reference was taken to the value, because it's not pinned within RefCell<T> itself.
If by "value" you mean the T then there's no way to get a pinned reference to it, because RefCell does not provide a way to do so. It doesn't need to be aware of that because it doesn't let that happen in the first place.
Yes, but then you wouldn’t be able to use a RefCellwithout pinning it. As I said above: one could make a separate “only-pinning RefCell ” type — but you can't have a single RefCell type that serves both purposes, because it is possible to get &T from Pin<&T>.
— Actually, an always-pin-projecting RefCell<T> could still be used unpinned with T: Unpin, but it would still add some inconvenience to use (calling Pin::new() all the time) so it makes sense to keep them separate for ergonomics.