2

I'm trying to create the following design for a pinned, self-referential struct:

use std::future::Future;
use std::marker::PhantomPinned;
use std::pin::Pin;

struct Data {}
struct DataResult {}

async fn my_async(data: &Data) -> DataResult {
    todo!()
}

/// Container for a pinned struct, where in inner future
/// contains a reference to self.data.
struct MyAsyncContainer<F: Future<Output = DataResult>> {
    data: Data,
    fut: Option<F>,
    unpin: PhantomPinned,
}

impl<F: Future<Output = DataResult>> MyAsyncContainer<F> {
    fn new(data: Data) -> Self {
        Self {
            data,
            fut: None,
            unpin: PhantomPinned,
        }
    }

    fn start<'a, A>(self: Pin<&'a mut Self>, func: A)
    where
        A: FnOnce(&'a Data) -> F,
    {
        let this = unsafe { self.get_unchecked_mut() };

        // ????
        let fut = func(&this.data);

        // self is pinned, but fut is not (yet),
        // so this should be safe
        this.fut = Some(fut);
    }
}

fn main() {
    let data = Data {};

    let mut container = MyAsyncContainer::new(data);
    let pinned = unsafe { Pin::new_unchecked(&mut container) };
    pinned.start(my_async);
}

The start function compiles, but when I try to compile the main function, I get the following error:

error[E0597]: `container` does not live long enough
  --> src/main.rs:48:46
   |
48 |     let pinned = unsafe { Pin::new_unchecked(&mut container) };
   |                                              ^^^^^^^^^^^^^^ borrowed value does not live long enough
49 |     pinned.start(my_async);
50 | }
   | -
   | |
   | `container` dropped here while still borrowed
   | borrow might be used here, when `container` is dropped and runs the destructor for type `MyAsyncContainer<impl std::future::Future>`

Is there a way to make a self-referential struct like this work correctly?

I did consider just using an async function, like so:

async fn my_async(data: &Data) -> DataResult {
    todo!()
}

async fn data_wrapper(data: Data) -> DataResult {
    my_async(&data).await
}

The problem is that I need the future type to be nameable, because it's being embedded in another struct as part of a larger async library.

I've read How to use the Pin struct with self-referential structures? but it didn't answer my question, though the points about raw pointers were helpful. The key difference here that I don't think is covered by those examples is the my_async function, which does need to take a piece of data by reference. I can't pass a pointer into it, and it's not clear to me how to transition the pointer to a reference with the right lifetime. In particular, my_async needs to be prevented from operating on &'static data and leaking the reference.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lucretiel
  • 3,145
  • 1
  • 24
  • 52

0 Answers0