Pinning trouble in a second wrapper, but not first

I have a crate foo that has implemented Future on SendFuture.

I have another crate bar that needs to add a thin wrapper around foo::SendFuture (mostly meant to convert error codes). In bar's source:

#[repr(transparent)]
pub struct SendFuture<E>(foo::SendFuture<E>);

impl<E> Future for SendFuture<E> {
  type Output = Result<(), Error<E>>;
  fn poll(
    mut self: Pin<&mut Self>,
    ctx: &mut Context<'_>
  ) -> Poll<Self::Output> {
    match Pin::new(&mut self.0).poll(ctx) {
      Poll::Ready(res) => Poll::Ready(res.map_err(Error::from)),
      Poll::Pending => Poll::Pending
    }
  }
}

Along comes the need for crate baz, which does mostly the same thing -- but it adds some features that requires another generic, but it is unused in the SendFuture wrapper. Rust needs the generic to be referenced so a PhantomData is added. This wrapper has mostly the role of converting bar::Error to baz::Error.

In baz's source:

pub struct SendFuture<R, E> {
  inner: bar::SendFuture<E>,
  _marker: PhantomData<R>
}

impl<R, E> Future for SendFuture<R, E> {
  type Output = Result<(), Error<E>>;
  fn poll(
    mut self: Pin<&mut Self>,
    ctx: &mut Context<'_>
  ) -> Poll<Self::Output> {
    match Pin::new(&mut self.inner).poll(ctx) {
      Poll::Ready(res) => Poll::Ready(res.map_err(Error::from)),
      Poll::Pending => Poll::Pending
    }
  }
}

Unlike the wrapper in bar, the one in baz does not compile:

error[E0596]: cannot borrow data in dereference of `Pin<&mut SendFuture<E>>` as mutable
   --> src/send.rs:177:20
    |
177 |     match Pin::new(&mut self.inner).poll(ctx) {
    |                    ^^^^^^^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut SendFuture<E>>`

The two are almost identical, except for bar's SendFuture having a single member and being repr(transparent), while baz's has multiple members (albeit one is a PhantomData). I thought I had a vague idea what pinning does, but I need someone to explain why baz's wrapper doesn't work.

XY: I know I can just stick the the call that returns SendFuture in baz in an async method and not bother doing the manual Future implementation. This question is just for curiosa.

Your problem probably has to do with how Unpin is automatically implemented for types, but I'm having trouble reproducing the problem exactly as you describe it.

I either get an error in baz mentioning SendFuture<R, E>

error[E0596]: cannot borrow data in dereference of `std::pin::Pin<&mut baz::SendFuture<R, E>>` as mutable
  --> src\lib.rs:79:28
   |
79 |             match Pin::new(&mut self.inner).poll(ctx) {
   |                            ^^^^^^^^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::pin::Pin<&mut baz::SendFuture<R, E>>`

Or I get an error in bar mentioning foo::SendFuture<E>

error[E0277]: `E` cannot be unpinned
   --> src\lib.rs:47:28
    |
47  |             match Pin::new(&mut self.0).poll(ctx) {
    |                   -------- ^^^^^^^^^^^ within `foo::SendFuture<E>`, the trait `std::marker::Unpin` is not implemented for `E`
    |                   |
    |                   required by a bound introduced by this call
    |
    = note: consider using the `pin!` macro
            consider using `Box::pin` if you need to access the pinned value outside of the current scope

Depending on how I use E in foo::SendFuture.

Here's the mock up of your situation I'm using

mod foo {
    use std::{future::Future, marker::PhantomData};

    pub struct SendFuture<E>(PhantomData<fn() -> E>);

    impl<E> Future for SendFuture<E> {
        type Output = Result<(), E>;

        fn poll(
            self: std::pin::Pin<&mut Self>,
            _cx: &mut std::task::Context<'_>,
        ) -> std::task::Poll<Self::Output> {
            todo!()
        }
    }
}

mod bar {
    use super::foo;
    use std::{
        future::Future,
        pin::Pin,
        task::{Context, Poll},
    };

    pub struct Error<E>(E);

    impl<E> Error<E> {
        pub fn into_inner(self) -> E {
            self.0
        }
    }

    impl<E> From<E> for Error<E> {
        fn from(value: E) -> Self {
            Self(value)
        }
    }

    #[repr(transparent)]
    pub struct SendFuture<E>(foo::SendFuture<E>);

    impl<E> Future for SendFuture<E> {
        type Output = Result<(), Error<E>>;
        fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
            match Pin::new(&mut self.0).poll(ctx) {
                Poll::Ready(res) => Poll::Ready(res.map_err(Error::from)),
                Poll::Pending => Poll::Pending,
            }
        }
    }
}

mod baz {
    use super::bar;
    use std::{
        future::Future,
        marker::PhantomData,
        pin::Pin,
        task::{Context, Poll},
    };
    pub struct Error<E>(E);

    impl<E> From<bar::Error<E>> for Error<E> {
        fn from(value: bar::Error<E>) -> Self {
            Self(value.into_inner())
        }
    }

    pub struct SendFuture<R, E> {
        inner: bar::SendFuture<E>,
        _marker: PhantomData<R>,
    }

    impl<R, E> Future for SendFuture<R, E> {
        type Output = Result<(), Error<E>>;
        fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
            match Pin::new(&mut self.inner).poll(ctx) {
                Poll::Ready(res) => Poll::Ready(res.map_err(Error::from)),
                Poll::Pending => Poll::Pending,
            }
        }
    }
}

All of that being said, you're having issues because you're asking for more than you technically need.

You can use a pin projection to avoid needing to have Unpin implemented. See Projections and Structural Pinning in the std::pin module docs for more about the safety of projections.

Playground

    pub struct SendFuture<R, E> {
        inner: bar::SendFuture<E>,
        _marker: PhantomData<R>,
    }

    impl<R, E> Future for SendFuture<R, E> {
        type Output = Result<(), Error<E>>;
        fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
            let inner = unsafe { self.map_unchecked_mut(|p| &mut p.inner) };
            match inner.poll(ctx) {
                Poll::Ready(res) => Poll::Ready(res.map_err(Error::from)),
                Poll::Pending => Poll::Pending,
            }
        }
    }

(Note: This assumes you don't have Drop implemented for baz::SendFuture. You can cause UB if you do implement it and move out of the pinned future field. See the Drop implementation sections of the std::pin module docs for more details).

If you'd like to not write the unsafe code yourself, you can use a crate like pin-project-lite to handle the unsafe parts in a sound way for you.

    pin_project_lite::pin_project! {
        pub struct SendFuture<R, E> {
            #[pin]
            inner: bar::SendFuture<E>,
            _marker: PhantomData<R>,
        }
    }

    impl<R, E> Future for SendFuture<R, E> {
        type Output = Result<(), Error<E>>;
        fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
            let this = self.project();
            match this.inner.poll(ctx) {
                Poll::Ready(res) => Poll::Ready(res.map_err(Error::from)),
                Poll::Pending => Poll::Pending,
            }
        }
    }

You could also constrain R and/or E with Unpin to ensure the future is Unpin, or you could box the future so it's unconditionally Unpin. Which of these options is applicable depends on details I'm not sure of about foo::SendFuture, but generally I would try to avoid requiring Unpin inside Future impls when you can use pin projection of some sort.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.