如何编写一个以一个闭包作为参数并以另一个闭包作为参数的函数?

Pio*_*ach 5 rust

我想编写一个将一个闭包作为参数的函数,该函数将另一个闭包作为参数,如下所示:

fn test<F: Fn(G), G: Fn()>(f: F) {
    let print = || {
        println!("hello");
    };
    f(print);
}

fn main() {
    test(|print| {
        print();
    })
}
Run Code Online (Sandbox Code Playgroud)

但这给出了这个错误:

error[E0308]: mismatched types
 --> src/main.rs:5:7
  |
1 | fn test<F: Fn(G), G: Fn()>(f: F) {
  |                   - expected this type parameter
2 |     let print = || {
  |                 -- the found closure
...
5 |     f(print);
  |     - ^^^^^ expected type parameter `G`, found closure
  |     |
  |     arguments to this function are incorrect
  |
  = note: expected type parameter `G`
                    found closure `{closure@src/main.rs:2:17: 2:19}`
Run Code Online (Sandbox Code Playgroud)

Sil*_*olo 5

fn test<F: Fn(G), G: Fn()>(f: F)\n
Run Code Online (Sandbox Code Playgroud)\n

这读作“test需要一个f: F,其中F是一个特定的函数类型,需要一个G,其中G是一个特定的函数类型”。但您不想F采用调用者选择的特定函数类型。你想要F获取任何可调用的函数。

\n

原则上,你想说的是这个。

\n
fn test<F: \xe2\x88\x80(G: Fn()) => Fn(G)>(f: F)\n
Run Code Online (Sandbox Code Playgroud)\n

当然,我在这里编写语法,但你想说的是“对于所有函数类型GF可以接受 aG并做一些有用的事情”。Rust 没有Rank-2 多态性,所以我们需要找到一个解决方法。

\n

其一,您只需支付间接成本并获取特征对象即可。

\n
fn test<F: Fn(Box<dyn Fn()>)>(f: F) {\n  let print = || {\n    println!("hello");\n  };\n  f(Box::new(print));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您已将“我不知道G是什么”问题从编译时转移到运行时,并将信息放入 vtable 中。这是可行的,并且它不会改变从 调用函数的方式main,但它确实需要在运行时进行虚拟查找,这对性能影响非常小。

\n

由于您的闭包捕获(至少在本示例中不是),因此您也可以仅在第二层获取函数指针。

\n
fn test<F: Fn(fn())>(f: F) {\n  let print = || {\n    println!("hello");\n  };\n  f(print);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

fn()注意里面的小写字母。这是函数指针类型fn而不是不可变的可调用对象Fn特征。

\n

这将您的内部函数 ( print) 限制为普通fn或非捕获闭包,这可能足以满足您的用例。

\n

  • 特征对象方法不需要分配,闭包可以采用 `&amp;dyn Fn()` 而不是 `Box&lt;dyn Fn()&gt;`:https://play.rust-lang.org/?version=stable&amp;mode=调试&amp;版本=2021&amp;要点=3d205875f841b3749760f961c03b0af2 (4认同)