返回函数
返回实际函数指针需要堆分配和包装器:
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
需要有一个具体的返回类型,即函数指针.pointed函数还需要有一个具体的返回类型,这对于async
函数来说是不可能的,因为它返回不透明类型.从理论上讲,可以将返回类型写为fn(&'a str) -> impl Future<Output=()> + 'a
,但这需要编译器进行更多的猜测,目前还不支持.
如果你同意Fn
而不是fn
,你可以go 掉包装纸:
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
值返回不同的函数,需要将闭包本身框起来,这是一个额外的堆分配:
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
为了简化事情,考虑返回future 本身而不是函数指针.这实际上是相同的,但更好,并且不需要堆分配:
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
no user code is getting executed时.函数调用返回Future
:当有人等待返回的future 时,实际的函数体将被执行only.
新函数的使用方法与前一个函数几乎相同:
// 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;
}
再次注意,在这两种情况下,当等待future 时,实际的some_async_func
个实体被执行only.
如果希望能够基于action
值调用不同的异步函数,则需要再次装箱:
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时间.