Tokio异步等待与特性

8
我希望在一个特质中编写异步函数,但由于尚未支持特质中的async fn,因此我正在尝试找到等效的方法接口。这是我在 Rust nightly(2019-01-01)中尝试的代码:

playground

#![feature(await_macro, async_await, futures_api)]
#[macro_use]
extern crate tokio;
use tokio::prelude::*;

trait T {
    async fn f();
}

fn main() {
}

error[E0706]: trait fns cannot be declared `async`
 --> src/main.rs:7:5
  |
7 |     async fn f();
  |     ^^^^^^^^^^^^^

我在某个地方读到过,async只是impl Future

trait T {
    fn f() -> impl futures::Future<Item = (), Error = ()>;
}

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
 --> src/lib.rs:2:15
  |
2 |     fn f() -> impl futures::Future<Item = (), Error = ()>;
  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

直接返回impl trait是不被允许的,所以我尝试了一个包装后的trait:

trait Resource {
    fn loaded(&self) -> bool;
    fn init(&mut self, auth: &str) -> Box<dyn std::future::Future<Output=()>>;
    fn prepare(&mut self, auth: &str) -> Box<dyn std::future::Future<Output=()>> {
        Box::new(async {
            if !self.loaded() {
                await!(*(self.init(auth)));
            }
        })
    }
}

[rustc] the size for values of type `dyn std::future::Future<Output=()>` cannot be known at compilation time

没有使用deref,我会得到一个错误,即Box<>不存在into_awaitable

我能否使用非Sized的impl Future*Box<Future>await!一起使用?什么是trait中异步函数的最合适接口?


“直接返回 impl trait 是不允许的” — 你为什么这么说? - Peter Hall
啊,你的意思是在 trait 中不允许这样做。是的,在这种情况下,你需要返回一个 trait 对象。 - Peter Hall
请查看如何创建[MCVE],然后[编辑]您的问题以包括它。 我们无法确定代码中存在哪些crates、types、traits、fields等,甚至不知道您使用的Rust版本! 尝试在Rust Playground上生成可以重现您的错误的内容,或者您可以在全新的Cargo项目中重现它。 还有一些Rust特定的MCVE提示 - Shepmaster
我应该为每个简短的示例包含一个 Rust Playground 链接吗? - mq7
2个回答

3

async 函数和 impl Trait 都不允许在特性中使用。您可以使用关联类型来实现类似的功能。以下是一些想法:


pub trait ResourceTrait {
    type FutType: Future<Output = ()>;

    fn prepare(&mut self, auth: &str) -> Self::Next;
}

目前实现这个功能有些棘手,因为一些必要的工具还没有可用、稳定或存在缺陷。

可以这样实现:

Original Answer翻译成"最初的回答"

impl ResourceTrait for Resource {
    type FutType = FutureObj<'static, ()>;

    fn prepare(&mut self, auth: &str) -> FutureObj<'static, ()> {
        FutureObj::new(Box::new(
            async move {
                // Do async things
                // You might get a lifetime issue here if trying to access auth,
                // since it's borrowed.
            }
        ))
    }
}

使用存在类型的另一种选择可能是:

最初的回答。
impl ResourceTrait for Resource {
    // this is required since the real type of the async function
    // is unnameable
    existential type FutType = Future<Output = ()>;

    fn prepare(&mut self, auth: &str) -> Self::FutType {
        async move {
            // Do async things. Might still encounter the same borrowing issues,
            // since the lifetime of the returned Future isn't coupled to the
            // lifetime of self.
            // The workaround is to make copies of all required fields and move
            // them into the Future
        }
    }
}


最初的回答: 这可能会起作用,也可能不起作用(因为该功能仍在开发中)。为了正确地借用类似于selfauth的参数并返回Future,我们可能还需要首先使用通用关联类型。为了解决self的借用问题,您可以尝试定义。
struct Resource {
    inner: Arc<ResourceInner>, // carries all actual state
}

这样你就可以将prepare中的inner复制并移动到Future中。


2
如果您可以接受返回一个包装的future,您可以使用async-trait crate:
#![feature(async_await)]

use async_trait::async_trait;

#[async_trait]
trait Advertisement {
    async fn run(&self);
}

struct Modal;

#[async_trait]
impl Advertisement for Modal {
    async fn run(&self) {
        self.render_fullscreen().await;
        for _ in 0..4u16 {
            remind_user_to_join_mailing_list().await;
        }
        self.hide_for_now().await;
    }
}

struct AutoplayingVideo {
    media_url: String,
}

#[async_trait]
impl Advertisement for AutoplayingVideo {
    async fn run(&self) {
        let stream = connect(&self.media_url).await;
        stream.play().await;

        // Video probably persuaded user to join our mailing list!
        Modal.run().await;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接