我想编写一个将一个闭包作为参数的函数,该函数将另一个闭包作为参数,如下所示:
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)
fn test<F: Fn(G), G: Fn()>(f: F)\nRun Code Online (Sandbox Code Playgroud)\n这读作“test需要一个f: F,其中F是一个特定的函数类型,需要一个G,其中G是一个特定的函数类型”。但您不想F采用调用者选择的特定函数类型。你想要F获取任何可调用的函数。
原则上,你想说的是这个。
\nfn test<F: \xe2\x88\x80(G: Fn()) => Fn(G)>(f: F)\nRun Code Online (Sandbox Code Playgroud)\n当然,我在这里编写语法,但你想说的是“对于所有函数类型G,F可以接受 aG并做一些有用的事情”。Rust 没有Rank-2 多态性,所以我们需要找到一个解决方法。
其一,您只需支付间接成本并获取特征对象即可。
\nfn test<F: Fn(Box<dyn Fn()>)>(f: F) {\n let print = || {\n println!("hello");\n };\n f(Box::new(print));\n}\nRun Code Online (Sandbox Code Playgroud)\n您已将“我不知道G是什么”问题从编译时转移到运行时,并将信息放入 vtable 中。这是可行的,并且它不会改变从 调用函数的方式main,但它确实需要在运行时进行虚拟查找,这对性能影响非常小。
由于您的闭包未捕获(至少在本示例中不是),因此您也可以仅在第二层获取函数指针。
\nfn test<F: Fn(fn())>(f: F) {\n let print = || {\n println!("hello");\n };\n f(print);\n}\nRun Code Online (Sandbox Code Playgroud)\nfn()注意里面的小写字母。这是函数指针类型fn,而不是不可变的可调用对象Fn特征。
这将您的内部函数 ( print) 限制为普通fn或非捕获闭包,这可能足以满足您的用例。