`RefCel`l example from `std::pin` docs incorrect?

I was reading up on pinning in the std::pin module documentation. In the section about structural pinning there is an example involving RefCell and a hypothetical method on it fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>.
It is supposed to be a more involved example of a type that provides structural pinning to one of its fields while also having other methods that allow violating the pinning guarantees.

I know this isn't a real example, as the hypothetical method on RefCell does not exist. However, apart from that, the rest of the code should be standard Rust. And I don't think it is.

Here is the example as of this post.

fn exploit_ref_cell<T>(rc: Pin<&mut RefCell<T>>) {
    // Here we get pinned access to the `T`.
    let _: Pin<&mut T> = rc.as_mut().get_pin_mut();

    // And here we have `&mut T` to the same data.
    let shared: &RefCell<T> = rc.into_ref().get_ref();
    let borrow = shared.borrow_mut();
    let content = &mut *borrow;
}

I see two issues with this example (aside from this being meant as a bad case).
Firstly, calling as_mut() on a Pin<&mut T> would require rc to be mut.
Secondly, the as_mut() is not needed, as this would just convert Pin<&mut RefCell<T>> to Pin<&mut RefCell<T>> using DerefMut of &mut T.

I guess my question is, are these issues really present, or am I missing something?

The point of the example is to show that having a RefCell::get_pin_mut() is wrong because it leads to undefined behavior in safe code. It doesn't matter how stupid the safe code is - if safe code leads to UB, then the API is wrong.

It shows this because if you can create a Pin<&mut T> to a value, and then create a &mut T to the same value, then by using the right type T, you can trigger undefined behavior. The example did not include actually triggering UB, but it's well known that being able to create a &mut T to a pinned value can be abused to trigger UB in safe code.

That's just a typo in the example. You can submit a PR to fix it by adding mut before rc in the function parameter.

The .as_mut() is required because otherwise calling get_pin_mut() would consume the Pin<&mut RefCell<T>>, resulting in a used-after-move error on the rc.into_ref() call.

3 Likes