.take() in impl Future (cannot borrow data in a dereference of Pin)

I'm trying to implement a really basic Future. Essentially, it wraps single value, while None the future is Pending, once Some it's Ready. The idea being I return the future and in some async block I modify this wrapped value so the returned Future then resolves.

I'm trying to take() the value out of the Option, but I get the following error. I (think?) get the gist, I'm trying to modify a Pinned value.

cannot borrow data in a dereference of `Pin<&mut ExampleFuture<A>>` as mutable
trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut ExampleFuture<A>>`

I don't really understand what I'm meant to do to get around this though. Every example using take() I can find uses an old model of Futures or simplified versions without Pin and so doesn't have any issues. Am I perhaps just approaching this the wrong way and take() isn't the right method? Is there a far better approach?

The reason I'm doing this is the function I'm trying to write returns a Stream and Future. It's a wrapper around an HTTP API, results are paginated (hence the Stream) but the first page of results has additional metrics I want to return in the Future. Happy to provide more detail if helpful.

Minimal & highly contrived example:

use futures::Future;
use std::{
  pin::Pin,
  task::{Context, Poll},
};

struct ExampleFuture<A> {
  result: Option<A>,
}

impl<A> Future for ExampleFuture<A> {
  type Output = A;

  fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
    if let Some(result) = self.result.take() {
      Poll::Ready(result)
    }
    else {
      Poll::Pending
    }
  }
}


async fn example() -> impl Future<Output = usize> {
  let mut future: ExampleFuture<usize> = ExampleFuture { result: None };

  async {
    // in my full use case this block is within `stream::try_unfold` 
    future.result = Some(42);
  };

  future
}

The automatic impl of the Unpin trait you get is the following:

impl<A: Unpin> Unpin for ExampleFuture<A> {}

So your future is only Unpin if A is Unpin. The problem is that to call take on the option, you need to obtain a mutable reference to it, but the mutable reference you have to self is pinned.

However you can only unwrap the Pin wrapper in safe code if your ExampleFuture is Unpin, but your Future impl applies to situations where it isn't, namely those where A isn't Unpin.

To solve this, add your own Unpin impl.

impl<A> Unpin for ExampleFuture<A> {}

This is correct because you never pin the self.result field.

1 Like

Fantastic, thank you, that does indeed solve it. I think I need to read more in to Pin to really understand the reason for this better.

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.