Why isn't `RefCell` unconditionally `Unpin`?

This is actually a bit confusing, because I wrote some replies to this post, which made it look like RefCell was unconditionally Unpin (I thought this to be true, because that was the simplest explanation). But after seeing this post and looking more deeply into the struct defs, I realized that it was related Pin having a Clone derive.

The above rambling is relevant, because it lead me to this:

// Note: the `Clone` derive below causes unsoundness as it's possible to implement
// `Clone` for mutable references.
// See <https://internals.rust-lang.org/t/unsoundness-in-pin/11311> for more details.
#[stable(feature = "pin", since = "1.33.0")]
#[lang = "pin"]
#[fundamental]
#[repr(transparent)]
#[rustc_pub_transparent]
#[derive(Copy, Clone)]
pub struct Pin<Ptr> {
    pointer: Ptr,
}

The key here is this thread: Unsoundness in `Pin` - language design - Rust Internals

In my understanding, the issue is how Unpin propagates. It's an auto trait, a struct is Unpin if all its fields are.

If RefCell<T>: Unpin unconditionally:

struct Wrapper<T> { 
    inner: RefCell<T> 
}
// Wrapper<T>: Unpin always (auto-derived from fields). Hypothetical:

let mut pinned: Pin<Box<Wrapper<NotUnpin>>> = Box::pin(...);
let wrapper: &mut Wrapper<NotUnpin> = pinned.as_mut().get_mut();  // allowed!
let inner: &mut NotUnpin = wrapper.inner.get_mut();
std::mem::swap(inner, &mut other);  // moved !Unpin out of Pin

RefCell gives &mut T, which lets you move T. That's why RefCell<T>: Unpin only when T: Unpin.

Compare with Rc<T> which is unconditionally Unpin, it oly gives &T, so you can't move the inner value.

This is actually part of a broader issue with Pin. Its guarantees are enforced by API contract, not compiler magic. There are multiple paths to circumvent it:

Path Mechanism Example
Unpin auto-trait Container becomes Unpin -> get_mut() -> &mut T what @Ddystopia and @Morgane55440 suggested, in the scenario where RefCell was unconditionally Unpin
Interior mutability &RefCell<T> -> borrow_mut() -> &mut T what you suggested in the second post
Custom DerefMut impl DerefMut for &'a Foo exploits Pin::as_mut()
Custom Clone impl Clone for &'a mut Foo exploits Pin::clone()

The last two are described in the original post of the internals thread.