我正在尝试创建一个以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 () -> ()
一起工作。但是,如果我将match
的None
臂与注释掉的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()
方法快,当然,如果稍后将其存储在结构体中,这将不起作用。