Как вручную реализовать закрепленную самоссылочную структуру? - PullRequest
2 голосов
/ 08 апреля 2020

Я пытаюсь создать следующий дизайн для закрепленной самореференциальной структуры:

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);
}

Функция start компилируется, но когда я пытаюсь скомпилировать функцию main, я получить следующую ошибку:

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>`

Есть ли способ заставить самоссылочную структуру, подобную этой, работать правильно?

Я действительно рассматривал просто использование функции asyn c, например, так :

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

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

Проблема в том, что мне нужно, чтобы будущий тип был именуемым, потому что он встроен в другую структуру как часть большой библиотеки asyn c.

У меня есть читать Как использовать структуру Pin с само-ссылочными структурами? но это не ответило на мой вопрос, хотя пункты о необработанных указателях были полезны. Ключевое отличие, которое, я думаю, не охватывается этими примерами, - это функция my_async, которая требует для получения части данных по ссылке. Я не могу передать указатель на него, и мне не ясно, как перевести указатель на ссылку с правильным временем жизни. В частности, необходимо запретить my_async работу на &'static data и утечку ссылки.

...