• Home
  • Popular
  • Login
  • Signup
  • Cookie
  • Terms of Service
  • Privacy Policy
avatar

Posted by John Dev


09 Jan, 2025

Updated at 20 Jan, 2025

How to project a pin through a field to an inner field?

pin-project/pin-project-lite is the primary way Rustaceans project a pin from a type, T, to one of its fields when the field is ?Unpin. Is there an idiomatic way to project a pin to a field of a field when the innermost field is ?Unpin?

use core::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};
use pin_project::pin_project;
struct Foo<T>(T);
#[pin_project]
struct Bar<T>(#[pin] Foo<T>);
impl<T: Future> Future for Bar<T> {
    type Output = T::Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // This is more concise and relies on the same amount of
        // `unsafe` code as the commented out portion.
        // SAFETY:
        // This is okay because `self.0.0` is pinned when `self` is.
        let p = unsafe { self.map_unchecked_mut(|f| &mut f.0 .0) };
        p.poll(cx)
        //let projected_foo = self.project().0;
        //let p = unsafe { projected_foo.map_unchecked_mut(|f| &mut f.0) };
        //p.poll(cx)
    }
}

I realize this is rather contrived. For example Foo would likely be defined in a separate crate; and unless Bar knows Foo doesn't implement Unpin for T: ?Unpin, then PhantomPinned would need to be added to Bar to prevent it from auto implementing Unpin.

In this example, I think § Choosing pinning to be structural for field… only needs to be amended such that "inner field" is substituted for "field" (e.g., item 1 requires Bar to not implement Unpin when Foo's field is ?Unpin).

Edit

The primary motivation behind this question is comprehension. Most (all?) of the time pin-project would be enough. I'm trying to understand if the rules mentioned in the pin module only apply to fields directly contained in the type or can they be generalized to any field in the object graph. If they can't be generalized, why?

There is nothing special about Foo either. If Bar is unable to project to Foo's T field, then that would seem to mean that even if Bar contained a primitive type that doesn't explicitly provide a pin projection method, it would be unable to project to the contained data. For example if Bar contained (T, T2), tuples don't provide pin projection methods; thus Bar would seemingly not be able to construct a Pin<&mut T>. I find that hard to believe.

13 posts - 5 participants

Read full topic