Как я могу сохранить асинхронную функцию в структуре и вызвать ее из экземпляра структуры? - PullRequest
2 голосов
/ 30 сентября 2019

Я пытаюсь добиться этого с помощью нового синтаксиса async / await, std::future::Future s и последней версии Tokio. Я использую Tokio 0.2.0-alpha.4 и Rust 1.39.0-nightly.

Разные вещи, которые я пробовал включать:

  • , используя Box<dyn> s для типов, которые я хочу сохранитьв структуре
  • с использованием обобщений в определении структуры

Я не мог получить минимальную рабочую версию, поэтому вот упрощенная версия того, чего я пытаюсь достичь:

async fn foo(x: u8) -> u8 {
    2 * x
}

// type StorableAsyncFn = Fn(u8) -> dyn Future<Output = u8>;

struct S {
    f: StorableAsyncFn,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let s = S { f: foo };

    let out = (s.f)(1).await;

    Ok(())
}

Конечно, этот код не компилируется со следующей ошибкой:

error[E0412]: cannot find type `StorableAsyncFn` in this scope

StorableAsyncFn здесь не определен, это тип, который я пытаюсь определить.

1 Ответ

3 голосов
/ 30 сентября 2019

Давайте использовать это как наш Минимальный воспроизводимый пример :

async fn foo(x: u8) -> u8 {
    2 * x
}

struct S {
    foo: (),
}

async fn example() {
    let s = S { foo };
}

Выдает ошибку:

error[E0308]: mismatched types
  --> src/main.rs:10:17
   |
10 |     let s = S { foo };
   |                 ^^^ expected (), found fn item
   |
   = note: expected type `()`
              found type `fn(u8) -> impl std::future::Future {foo}`

Тип foo являетсяуказатель на функцию, который принимает u8 и возвращает некоторый тип, реализующий черту std::future::Future. async fn фактически является просто синтаксическим сахаром, который преобразует -> Foo в -> impl Future<Output = Foo>.

Мы делаем нашу структуру универсальной и накладываем черту, привязанную к универсальному, который соответствует. В реальном коде вы, вероятно, захотите наложить ограничение на связанный тип Output, но в этом примере это не требуется. Затем мы можем вызвать функцию, как любое другое поле вызываемого члена:

async fn foo(x: u8) -> u8 {
    2 * x
}

struct S<F>
where
    F: std::future::Future,
{
    foo: fn(u8) -> F,
}

impl<F> S<F>
where
    F: std::future::Future,
{
    async fn do_thing(self) {
        (self.foo)(42).await;
    }
}

async fn example() {
    let s = S { foo };
    s.do_thing().await;
}

Чтобы быть еще более гибким, вы можете использовать другой универсальный тип для хранения замыкания вместо принудительного указания только указателя функции:

struct S<C, F>
where
    C: Fn(u8) -> F,
    F: std::future::Future,
{
    foo: C,
}

impl<C, F> S<C, F>
where
    C: Fn(u8) -> F,
    F: std::future::Future,
{
    async fn do_thing(self) {
        (self.foo)(42).await;
    }
}

См. Также:

...