首页 > 解决方案 > 是否可以为异步函数创建类型别名?

问题描述

我想创建一个类型别名来提升函数的闭包,而用户不必考虑函数的参数和返回签名。这将翻译这段代码:

async fn some_func(s: &mut Context<Props>) -> VNode {
    // some function body...
}

进入这段代码:

static SomeFunc: FC<Props> = |ctx| async {
    // some function body
};

在 JavaScript 中,这将使用 const 闭包函数定义而不是常规函数简写。

对于常规功能,这很好用:

type FC<T: Props> = fn(&mut Context<T>) -> Vnode;

然后,静态闭包被提升为函数指针。

但是,impl Future不能在类型别名中使用(甚至不能在 1.51 nightly 中使用),而且我也不能在类型别名中使用通用特征边界。我不知道这是否可能,但我很好奇类型别名是否有办法为异步 fns 工作。

为什么?

我正在设计的 API 将函数作为输入(不是结构或特征对象),我想让它易于使用。

标签: rustasync-await

解决方案


您不能直接执行此操作,因为每个 async fn 都有不同的返回类型,即使未来计算为相同的类型。这是因为每个 async fn 都有它自己Future返回的唯一类型。

为此,您必须使用特征对象 ( dyn Fn) 而不是函数指针 ( fn()) 作为类型。您正在寻找的类型是

use futures::future::BoxFuture;

type FC<T> = Box<dyn Send + Sync + for<'a> Fn(&'a mut Context<T>) -> BoxFuture<'a, Vnode>>;

上述类型将作为返回 a 的函数Future,但需要一个转换步骤才能从普通的 async fn 转换为上述类型的函数。

可以按如下方式执行此转换:

Box::new(|context| Box::pin(my_async_fn(context)))

它也可以使用期货箱来编写。在某些情况下,使用的版本.boxed()将有助于类型检查器并避免该Box::pin版本可能给出的一些错误。

use futures::future::FutureExt;

Box::new(|context| my_async_fn(context).boxed())

由于我们在这种情况下使用的特定类型别名涉及生命周期,因此无法为上述定义方便的转换。也就是说,当参数不是引用时,您可以这样做:

use futures::future::BoxFuture;

type AsyncFnPtr = Box<dyn Send + Sync + Fn(SomeArg) -> BoxFuture<'static, RetValue>>;

fn convert<F, Fut>(func: F) -> AsyncFnPtr
where
    F: Send + Sync + 'static,
    F: Fn(SomeArg) -> Fut,
    Fut: Send + 'static,
    Fut: Future<Output = RetValue>,
{
    Box::new(|context| Box::pin(func(context)))
}

其他资源:

  1. 是什么for<'a>意思?
  2. 为什么不会+ 'static导致内存泄漏?
  3. 关于同一主题的 URLO 线程。

推荐阅读