Open
Description
I think pin-project could provide a safe method from Pin<&mut Option<S>>
to Option<SProjectionOwned>
which is a mix of Option::take
, Pin::set(..., None)
and project_replace
. I imagine it could look something like this:
#[pin_project(take_unpinned)]
struct Inner<Pinned, Unpinned> {
#[pin]
pinned: Pinned,
unpinned: Unpinned,
}
#[pin_project]
struct Outer<Pinned, Unpinned> {
#[pin]
inner: Option<Inner<Pinned, Unpinned>>,
}
// Generated method
impl<Pinned, Unpinned> Inner<Pinned, Unpinned> {
fn take_unpinned(this: Pin<&mut Option<Self>>) -> Option<InnerProjectionOwned> {
// - Check if this points to None and bail out.
// - Drop pinned fields in place
// - Ptr read unpinned fields
// - Replace this with None using ptr write
// - Return unpinned fields
todo!()
}
}
The following is an example where this could be useful (here take_output
is what pin_project
could provide a generic version of):
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
time::Duration,
};
use pin_project::pin_project;
#[pin_project]
struct DelayOutput<T, Fut: Future<Output = T>> {
#[pin]
fut: Fut,
#[pin]
waiting: Option<DelayOutputInner<T>>,
}
impl<T, Fut: Future<Output = T>> DelayOutput<T, Fut> {
fn new(fut: Fut) -> Self {
Self { fut, waiting: None }
}
}
#[pin_project]
struct DelayOutputInner<T> {
output: T,
#[pin]
sleep: tokio::time::Sleep,
}
impl<T> DelayOutputInner<T> {
fn take_output(mut this: Pin<&mut Option<Self>>) -> Option<T> {
let (inner_ptr, opt_ptr) = {
let opt_ref = unsafe { this.as_mut().get_unchecked_mut() };
let Some(inner_ref) = opt_ref else {
return None;
};
let ptrs = (&raw mut *inner_ref, &raw mut *opt_ref);
_ = this;
ptrs
};
let output = unsafe { (&raw mut (*inner_ptr).output).read() };
unsafe { (&raw mut (*inner_ptr).sleep).drop_in_place() };
unsafe { opt_ptr.write(None) };
Some(output)
}
}
impl<T, Fut: Future<Output = T>> Future for DelayOutput<T, Fut> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
loop {
if let Some(waiting) = this.waiting.as_mut().as_pin_mut() {
match waiting.project().sleep.poll(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(_) => {
let output = DelayOutputInner::take_output(this.waiting);
return Poll::Ready(output.unwrap());
}
}
}
match this.fut.as_mut().poll(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(output) => {
this.waiting.set(Some(DelayOutputInner {
output,
sleep: tokio::time::sleep(Duration::from_secs(1)),
}));
}
}
}
}
}