不能设置默认的函数闭包



我正在尝试创建一个以Option<impl Fn() -> ()>为参数的方法,并且,如果此参数是Some(func),则将func存储为闭包,但是,如果它是None,则存储一些默认函数作为闭包。具体来说,我的代码如下:

fn foo() 
{
println!("HI");
}
fn bar(baz: Option<impl Fn () -> ()>)
{
let func = match baz {
Some(func) => func,
//None => foo,
None => unimplemented!()
};
func();
}
pub fn main() {
bar(Some(foo));
}

按原样编译,这向我表明foo确实应该与impl Fn () -> ()一起工作。但是,如果我将matchNone臂与注释掉的None臂交换,以将foo设置为"默认实现",则会得到以下错误:

error[E0308]: `match` arms have incompatible types
--> <source>:10:17
|
6  |   fn bar(baz: Option<impl Fn () -> ()>)
|                      ---------------- this type parameter
7  |   {
8  |       let func = match baz {
|  ________________-
9  | |         Some(func) => func,
| |                       ---- this is found to be of type `impl Fn() -> ()`
10 | |         None => foo,
| |                 ^^^ expected type parameter `impl Fn() -> ()`, found fn item
11 | |         //None => unimplemented!()
12 | |     };
| |_____- `match` arms have incompatible types
|
= note: expected type `impl Fn() -> ()`
found fn item `fn() {foo}`
error: aborting due to previous error

据我所知,从错误中可以看出,它说foo在函数中不符合impl Fn() -> ()的条件(尽管它作为参数传递时符合)。我在这里错过了什么导致这种情况,是否有一种方法来完成我想要的东西(将可选的闭包作为参数,并使用默认函数作为闭包,如果没有提供)?

match语句的所有分支必须解析为完全相同的类型.

考虑到这一点,让我们用它表示的泛型替换impl Fn() -> ()(顺便说一句,它也与impl Fn()相同):

fn bar<T>(baz: Option<T>) where T: Fn() {
// ...
}

impl Trait(在函数参数中)是常规泛型的语法糖,所以这是编译器看到的。

让我们看一下匹配:

fn bar<T>(baz: Option<T>) where T: Fn() {
let f = match baz {
Some(f) => f,
None => foo,
};
}

这行不通,因为foo的类型是一个特殊的东西,叫做"函数项"。(本质上是唯一的类型),但是f的类型是一个泛型参数T:它可以是任何可以想象的类型(只要它实现了Fn())。显然,它们可能是不同的,因此Rust以"具有不兼容的类型"为由拒绝匹配语句。

你正在寻找的是一个trait对象(尽管这是以堆分配和虚函数表调度为代价的):

fn bar(baz: Option<impl Fn()>) {
let f: Box<dyn Fn()> = match baz {
Some(f) => Box::new(f),
None => Box::new(foo),
};
f();
}

或者,您可以立即调用该函数,而不将其存储在变量中:

fn bar(baz: Option<impl Fn()>) {
match baz {
Some(f) => f(),
None => foo(),
};
}

这可能比上面的dyn Fn()方法快,当然,如果稍后将其存储在结构体中,这将不起作用。

最新更新