第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部分:如果基于动作参数,高阶函数必须返回async函数1或async函数2,那么sig应该是什么.

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

推荐答案

返回函数

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

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_funcno 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时间.

Rust相关问答推荐

如何容器化Linux上基于Rust的Windows应用程序的编译过程?

是否有可能同时避免不兼容的不透明类型和代码重复?

为什么我不能从带有字符串的 struct 的引用迭代器中收集VEC<;&;str&>?

为什么Rust函数的移植速度比C++慢2倍?

获取字符串切片(&;str)上的切片([ia..ib])返回字符串

制作一片连续整数的惯用Rust 方法?

无法定义名为&new&的关联函数,该函数的第一个参数不是self

如何高效地将 struct 向量中的字段收集到单独的数组中

Rust面向对象设计模式

如何将一个矩阵的列分配给另一个矩阵,纳尔代数?

如何在 Rust 中打印 let-else 语句中的错误?

Rust 并行获取对 ndarray 的每个元素的可变引用

sha256 摘要仅适用于 &*

如何为整数切片定义一个带有额外函数的特性别名?

使用 Rust 从 Raspberry Pi Pico 上的 SPI 读取值

当你删除一个存在于堆栈中的值时,为什么 rust 不会抱怨

Rust 函数指针似乎被borrow 判断器视为有状态的

使用 serde_json 进一步处理字段

深度嵌套枚举的清洁匹配臂

如何重写这个通用参数?