Rust:传递异步函数指针

Dud*_*ude 3 rust

假设我们有这样的代码:

fn inc(src: u32) -> u32 {
    src + 1
}

fn inc2(src: u32) -> u32 {
    src + 2
}

type Incrementer = fn(u32) -> u32;
fn increment_printer(inc: Incrementer) {
    println!("{}", inc(1));
}

fn main() {
    increment_printer(inc);
    increment_printer(inc2);
}
Run Code Online (Sandbox Code Playgroud)

两个函数具有相同的签名,第三个函数接受它们的指针。运行此代码会打印 2\n3。

但类似的东西不会编译:

use core::future::Future;

async fn inc(src: u32) -> u32 {
    src + 1
}

async fn inc2(src: u32) -> u32 {
    src + 2
}

type Incrementer = fn(u32) -> dyn Future<Output = u32>;
async fn increment_printer(inc: Incrementer) {
    println!("{}", inc(1).await);
}

fn main() {
    async {
        increment_printer(inc).await;
        increment_printer(inc2).await;
    }
}
Run Code Online (Sandbox Code Playgroud)
18 |         increment_printer(inc).await;
   |                           ^^^ expected trait object `dyn Future`, found opaque type
   |
   = note: expected fn pointer `fn(_) -> (dyn Future<Output = u32> + 'static)`
                 found fn item `fn(_) -> impl Future {inc}`
Run Code Online (Sandbox Code Playgroud)

我知道每个异步函数都有自己的类型,如错误中所述。是否可以强制编译器忘记具体类型并将它们视为相似类型?

也许可以强制异步函数返回Box<Future<Output=u32>>?

我不想为了方便而放弃异步函数。另外,我不想在将异步函数指针传递给函数之前要求调用 Box::pin() 。

知道是否有更多选择会很有趣。

use*_*342 8

也许可以强制异步函数返回 Box<Future<Output=u32>> ?

不是真的,但可以将函数包装到一个调用该函数并返回一个装箱的 future 的闭包中。当然,闭包本身需要装箱,以便像这样的函数increment_printer可以接收它,但这两种装箱都可以封装在实用函数中。例如(游乐场):

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

async fn inc(src: u32) -> u32 {
    src + 1
}

async fn inc2(src: u32) -> u32 {
    src + 2
}

type Incrementer = Box<dyn FnOnce(u32) -> Pin<Box<dyn Future<Output = u32>>>>;

fn force_boxed<T>(f: fn(u32) -> T) -> Incrementer
where
    T: Future<Output = u32> + 'static,
{
    Box::new(move |n| Box::pin(f(n)))
}

async fn increment_printer(inc: Incrementer) {
    println!("{}", inc(1).await);
}

fn main() {
    async {
        increment_printer(force_boxed(inc)).await;
        increment_printer(force_boxed(inc2)).await;
    };
}
Run Code Online (Sandbox Code Playgroud)

如果你有能力制作像increment_printer泛型这样的函数,那么你就可以摆脱外框(playground):

// emulate trait alias
trait Incrementer: FnOnce(u32) -> Pin<Box<dyn Future<Output = u32>>> {}
impl<T> Incrementer for T
    where T: FnOnce(u32) -> Pin<Box<dyn Future<Output = u32>>>
{
}

fn force_boxed<T>(f: fn(u32) -> T) -> impl Incrementer
where
    T: Future<Output = u32> + 'static,
{
    move |n| Box::pin(f(n)) as _
}

async fn increment_printer(inc: impl Incrementer) {
    println!("{}", inc(1).await);
}
Run Code Online (Sandbox Code Playgroud)

我不想为了方便而放弃异步函数。另外,我不想Box::pin()在将异步函数指针传递给函数之前要求调用。

您需要Box::pin() 在某个地方进行调用- 上面的调用至少限于一个地方,并且您会得到一种Incrementer类型,就像异步函数的接收者通过force_boxed().

  • [这里有一个基于这个答案的想法](https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=8520a3bd55e7adeb5ebe2dbb293bef4a)允许使用“Incrementer”的函数指针,所以你不需要泛型(但你*确实*需要一个宏来按需包装`async fn`)。 (2认同)