第1部分:返回异步函数的函数的签名应该是什么?
pub async fn some_async_func(arg: &str) {}
// What should be sig here?
pub fn higher_order_func(action: &str) -> ???
{
some_async_func
}
第2部分:如果基于action参数,higher_order_func必须返回async_func1或async_func2,那么sig应该是什么。
我也很想知道在有多种解决方案的情况下如何权衡性能。请注意,我希望返回函数本身作为fn指针或Fn* trait,而不是调用它的结果。
3条答案
按热度按时间bpzcxfmw1#
返回函数
返回实际的函数指针需要堆分配和 Package 器:
higher_order_func
需要有一个具体的返回类型,即函数指针。被指向的函数也需要有一个具体的返回类型,这对于async
函数是不可能的,因为它返回不透明类型。理论上,可以将返回类型写为fn(&'a str) -> impl Future<Output=()> + 'a
,但这需要编译器进行更多的猜测,目前不支持。如果您可以使用
Fn
代替fn
,那么可以去掉 Package 器:要根据
action
值返回不同的函数,您需要将闭包本身装箱,这又是一个堆分配:替代品:回报未来
为了简化,考虑返回future本身而不是函数指针,这实际上是相同的,但是更好,并且不需要堆分配:
当
higher_order_func_future
被调用时,看起来好像some_async_func
正在被执行--但事实并非如此。由于异步函数的工作方式,当你调用some_async_func
时,没有用户代码正在被执行。函数调用返回Future
:实际的函数体将仅在某人等待返回的future时被执行。新函数的使用方法与上一个函数几乎相同:
再次注意,在这两种情况下,实际的
some_async_func
主体仅在等待将来时执行。如果你想根据
action
值调用不同的异步函数,你需要再次装箱:尽管如此,这只是一次堆分配,所以我强烈建议返回一个future。我能想象到的唯一一种情况是,当你想把装箱的闭包保存在某个地方并多次使用它时,前面的解决方案更好。在这种情况下,过度分配只发生一次,当你进行闭包时,你可以通过只调度一次基于
action
的调用来节省一些CPU时间。jdzmm42g2#
理想情况下,您需要的是一个嵌套的impl trait:
-> impl Fn(&str) -> impl Future<Output = ()>
。但是不支持嵌套的impl trait。但是,您可以使用trait模拟它。我们的想法是定义一个trait来抽象“返回未来的函数”的概念,例如,如果我们的函数接受
u32
,它可能看起来像:然后我们取
impl AsyncFn
,试着把它简单地应用到&str
doesn't work上:这个错误可能看起来很奇怪,但是它是由
async fn
返回一个未来值的事实引起的,这个未来值是由它的所有参数的生存期所限定的,也就是说,对于一个签名async fn foo<'a>(arg: &'a str)
,未来值不是impl Future<Output = ()>
而是impl Future<Output = ()> + 'a
。有一种方法可以在我们的trait中捕捉这种关系,我们只需要使它通用于参数并使用HRTB:然后我们将类型指定为:
n53p2ov03#
除了公认的答案之外,根据您的用例,还可以“伪造”高阶函数,并通过使用一个简单的宏就地扩展 Package 器代码来避免任何堆分配: