Feeling generally comfortable with pinning and futures, but there's one point niggling at me. Future::poll()
accepts a Pin
-- not a &Pin
, not a &mut Pin
or anything like that:
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
To me, this guarantees that the thing being pointed to won't move during the execution of poll(), but I don't see how it guarantees it won't move in between calls to poll()
.
In this code snippet, I pin a mutable reference, consume the pin, move the thing being referenced, construct another pin... no problem:
fn foo<T: Display>(x: Pin<&mut T>) {
println!("foo({})", x)
}
...
let mut x = 1;
let px: Pin<&mut i32> = Pin::new(&mut x);
foo(px);
let mut y = 2;
std::mem::swap(&mut x, &mut y);
let rx: Pin<&mut i32> = Pin::new(&mut x);
foo(rx);
This produces: "foo(1)" and "foo(2)".
What am I missing, here? In what way does the signature of poll()
prevent an async runtime from moving the future implementation?
Addition: Reading further (e.g. [Pin use in Futures::poll - #7 by kpreid]), it seems it doesn't: the function signature requires you to get a Pin. If the pointee is Unpin, this is no big deal-- you can safely do just what I did above. If the pointee is Unpin, however, you have to get the Pin via new_unchecked()
which makes you promise that "...the data will not be moved or its storage invalidated until it gets dropped." So in the second case, by obtaining the Pin, I'm committing to never moving the pointee, right?
IOW-- what is the contract offered by Pin::new_unchecked()
? Can I move the pointee after my Pin is dropped?