从Rust中的函数返回异步函数 [英] Return an async function from a function in Rust

查看:102
本文介绍了从Rust中的函数返回异步函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

第1部分:返回异步函数的函数的签名应该是什么?

Part 1: What should be the signature of a function returning an async function?

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,则信号应该是什么.

Part 2: What should be the sig, if based on the action parameter, higher_order_func had to return either async_func1 or async_func2.

如果有多种解决方案,我也有兴趣学习性能权衡.请注意,我想将函数本身作为fn指针或Fn *特征返回,而不是调用它的结果.

I am also interested in learning the performance tradeoffs if there are multiple solutions. Please note that I'd like to return the function itself as an fn pointer or an Fn* trait, and not the result of invoking it.

推荐答案

返回函数

返回实际的函数指针需要堆分配和包装器:

Returning a function

Returning the actual function pointer requires heap allocation and a wrapper:

use std::future::Future;
use std::pin::Pin;

pub async fn some_async_func(arg: &str) {}

pub fn some_async_func_wrapper<'a>(arg: &'a str)
    -> Pin<Box<dyn Future<Output=()> + 'a>>
{
    Box::pin(some_async_func(arg))
}

pub fn higher_order_func<'a>(action: &str)
    -> fn(&'a str) -> Pin<Box<dyn Future<Output=()> + 'a>>
{
    some_async_func_wrapper
}

为什么要拳击? higher_order_func 需要具有具体的返回类型,即函数指针.指向函数还必须具有具体的返回类型,这对于 async 函数而言是不可能的,因为它返回的是不透明类型.从理论上讲,可以将返回类型写为 fn(&'a str)->表示Future< Output =()>+'a ,但这将需要编译器进行更多猜测,并且目前不支持.

Why boxing? higher_order_func needs to have a concrete return type, which is a function pointer. The pointed function needs to also have a concrete return type, which is impossible for async function since it returns opaque type. In theory, it could be possible to write return type as fn(&'a str) -> impl Future<Output=()> + 'a, but this would require much more guesswork from the compiler and currently is not supported.

如果您对 Fn 而不是 fn 没问题,则可以摆脱包装器:

If you are OK with Fn instead of fn, you can get rid of the wrapper:

pub async fn some_async_func(arg: &str) {}

pub fn higher_order_func<'a>(action: &str)
    -> impl Fn(&'a str) -> Pin<Box<dyn Future<Output=()> + 'a>>
{
    |arg: &'a str| {
        Box::pin(some_async_func(arg))
    }
}

要基于 action 值返回不同的函数,您需要将闭包本身装箱,这是另外一个堆分配:

To return a different function based on action value, you will need to box the closure itself, which is one more heap allocation:

pub async fn some_async_func_one(arg: &str) {}
pub async fn some_async_func_two(arg: &str) {}

pub fn higher_order_func<'a>(action: &str)
    -> Box<dyn Fn(&'a str) -> Pin<Box<dyn Future<Output=()> + 'a>>>
{
    if action.starts_with("one") {
        Box::new(|arg: &'a str| {
            Box::pin(some_async_func_one(arg))
        })
    } else {
        Box::new(|arg: &'a str| {
            Box::pin(some_async_func_two(arg))
        })
    }
}

替代:返回未来

为简化起见,请考虑返回future本身而不是函数指针.这实际上是相同的,但是更好,并且不需要堆分配:

Alternative: returning a future

To simplify things, consider returning a future itself instead of a function pointer. This is virtually the same, but much nicer and does not require heap allocation:

pub async fn some_async_func(arg: &str) {}

pub fn higher_order_func_future<'a>(action: &str, arg: &'a str)
    -> impl Future<Output=()> + 'a
{
    some_async_func(arg)
}

看起来像是,当调用 higher_order_func_future 时,正在执行 some_async_func -但这不是事实.由于异步功能的工作方式,当您调用 some_async_func 时,不会执行用户代码.该函数调用返回一个 Future :仅当某人等待返回的将来时,才会真正执行该函数主体.

It might look like, when higher_order_func_future is called, some_async_func is getting executed - but this is not the case. Because of the way async functions work, when you call some_async_func, no user code is getting executed. The function call returns a Future: the actual function body will be executed only when someone awaits the returned future.

您可以使用与以前功能几乎相同的方式使用新功能:

You can use the new function almost the same way as the previous function:

// With higher order function returning function pointer
async fn my_function() {
    let action = "one";
    let arg = "hello";
    higher_order_func(action)(arg).await;
}

// With higher order function returning future
async fn my_function() {
    let action = "one";
    let arg = "hello";
    higher_order_func_future(action, arg).await;
}

再次请注意,在两种情况下,仅在等待将来时才实际执行 some_async_func 正文.

Notice, once more, that in both cases the actual some_async_func body is executed only when the future is awaited.

如果您希望能够基于 action 值调用不同的异步函数,则需要再次装箱:

If you wanted to be able to call different async functions based on action value, you need boxing again:

pub async fn some_async_func_one(arg: &str) {}
pub async fn some_async_func_two(arg: &str) {}

pub fn higher_order_func_future<'a>(action: &str, arg: &'a str)
    -> Pin<Box<dyn Future<Output=()> + 'a>>
{
    if action.starts_with("one") {
        Box::pin(some_async_func_one(arg))
    } else {
        Box::pin(some_async_func_two(arg))
    }
}

不过,这只是一个堆分配,因此我强烈建议返回一个Future.我可以想像的唯一方案是,您想将盒装封闭保存在某个地方并多次使用时,哪种解决方案更好.在这种情况下,过多的分配只会发生一次,并且通过基于 action 调度一次(在您进行关闭操作时)只会调度一次,从而节省了一些CPU时间.

Still, this is just one heap allocation, so I strongly advise returning a future. The only scenario that I can imagine where the previous solution is better is when you want to save the boxed closure somewhere and use it many times. In this case, excessive allocation happens only once, and you spare some CPU time by dispatching the call based on action only once - when you make the closure.

这篇关于从Rust中的函数返回异步函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆