I fixed my playground link (was a link to last saved playground instead of my specific playground).
I meant Pin::get_unchecked_mut but got it wrong. I fixed that in my post.
CAD97 explains this in their answer.
Agreed. I did understand the difference between the pointer P that Pin wraps and the "pointed to value T" (aka <P as Deref>::Target in the docs), but that got lost in my explanations. I can see how this makes these explanations confusing, because I used "Pin<T> where T is !Unpin" as a means to say: "Pin<P> where <P as Deref>::Target is !Unpin", and “Pin<&mut T>” as a means to say "Pin<P> where P=&mut T for some T". Duly noted for further discussions.
Thank you for this explanation! This clears up my misunderstanding. Where is this precise guarantee stated in the pin module documentation? The closest I can find is the Safety § for get_unchecked_mut that states:
[…] You must guarantee that you will never move the data out of the mutable reference you receive when you call this function, so that the invariants on the Pin type can be upheld.
but it is limited to the returned mutable reference (in particular, it says nothing of the preexisting mutable reference of my playground example).
So to restate the Pin guarantee you expressed, once I create a Pin<P>, the value pointed by my P instance is expected to never move again (unless the pointed to is Unpin of course)?
If this is exactly so, then it looks to me like the Pin type is very limiting, wrt what we can do with it from safe code. Basically I was thinking of Pin as some kind of &mut T with limitations, that would apply for there entire’s life of the Pin but not beyond. Something like the following:
// impl block when P = &mut T for some T
// this Pin only uphelds the guarantee that T is not moved if Pin is observed, for the duration that it is observable.
impl<'pin, T> Pin<&'pin mut T> {
/// safe to create, we wrapped our only &mut in existence into the Pin
fn new(x: &'pin mut T) -> Self;
/// Difference to the existing `get_mut`
/// 1. borrows the Pin rather than consuming it.
/// 2. returns a "wrapper" around &mut T rather than a naked &mut T.
/// The wrapper impl DerefMut and is only there as a way to differenciate refs obtained from this method from refs obtained from the next. Using DerefMut through this wrapper is semantically unsafe, but since there is no way to ensure that from the wrapper, it is the `get_mut` method itself that must be `unsafe`.
unsafe fn get_mut<'a, 'lock : 'a>(&'a mut self) -> PinnedMut<'a, T>;
/// "unpins" T, removing the guarantee that it won't move, and giving back a naked mut ref as a result. It is safe to move out of the returned mut ref.
/// For this to be safe, Pin should provide, like &mut, no means of "copying" it.
/// There's currently a loophole due to Pin::as_mut that borrows a `Pin` and returns a new Pin that can safely be consumed...
fn into_mut(self) -> &'lock mut T;
}
From what you’re saying I now understand that Pin is rather a line of demarcation, after which the pointed to value will never move again. If that’s so, it seems to me that the Pin type provides too strong of a guarantee, which limits its uses. Such “strong guarantee” available forever from the moment you create a Pin strikes me as inconsistent with the rest of Rust’s affine type system. Was an API similar to my above code considered (with a weaker guarantee on Pin, but also less unsafety on its usage), and if so, why was it rejected? I remember reading lots of blog articles on the design of Pin, and that it answers a problem very strongly constrained, hence the strong guarantee is maybe needed and surely there’s something I’m missing again?
I’m not sure I understand this statement. How is that unsafe code “knowing what it is doing” if it is violating the safety guarantees of Pin? Won’t that also ruin the guarantees of Pin for safe users, hence defeating the whole point of unsafe?