"Expected fn item, found a different fn item" 使用函数指针时
"Expected fn item, found a different fn item" when working with function pointers
我有以下代码 (Playground):
// Two dummy functions, both with the signature `fn(u32) -> bool`
fn foo(x: u32) -> bool {
x % 2 == 0
}
fn bar(x: u32) -> bool {
x == 27
}
fn call_both<F>(a: F, b: F)
where
F: Fn(u32) -> bool,
{
a(5);
b(5);
}
fn main() {
call_both(foo, bar); // <-- error
}
对我来说,这似乎应该编译为 foo
和 bar
具有相同的签名:fn(u32) -> bool
。然而,我收到以下错误:
error[E0308]: mismatched types
--> src/main.rs:20:20
|
20 | call_both(foo, bar);
| ^^^ expected fn item, found a different fn item
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool {bar}`
使用此代码可以触发相同的错误:
let mut x = foo;
x = bar; // <-- error
我也试过再次将bar
转换为函数指针类型:
let mut x = foo;
x = bar as fn(u32) -> bool; // <-- error
这导致了一个稍微不同的错误:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | x = bar as fn(u32) -> bool;
| ^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool`
我完全不明白这些错误。 什么是 fn 项与 fn 指针,为什么 foo
和 bar
不同的 fn 项?
自 Rust PR #19891 合并以来,每个命名函数都有不同的类型。但是,您可以使用 as
运算符将函数转换为相应的函数指针类型。
call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);
仅转换第一个函数也是有效的:将在第二个函数上推断转换,因为两个函数必须具有相同的类型。
call_both(foo as fn(u32) -> bool, bar);
函数项与函数指针
当您通过名称引用函数时,您获得的类型不是函数指针(例如 fn(u32) -> bool
)。相反,您会得到函数的 项类型 的零大小值(例如 fn(u32) -> bool {foo}
)。
该值大小为零,因为它不存储实际的函数指针。类型完美地标识了函数,因此无需在类型中存储实际数据。这有几个优点,主要是关于更容易优化。函数指针就像您对其他语言的期望一样:它存储函数的地址。
一个函数指针通过存储地址指向函数; 函数项通过类型信息引用函数。
在许多情况下可以将函数项强制转换为函数指针,例如:作为函数的参数和在 let _: fn(u32) -> bool = foo;
语句中。此外,您可以将函数项显式转换为函数指针:foo as fn(u32) -> bool
.
您可以在 function items, function pointers and coercion 上的参考资料中阅读有关此主题的更多信息。
解决您的问题
在您的情况下,编译器不够聪明,无法确定您想要 foo
和 bar
中的函数指针而不是函数项。当您调用 call_both(foo, bar)
时,编译器将泛型类型 F
设置为 fn(u32) -> bool {foo}
,因为这是第一个参数的类型。然后它抱怨第二个参数没有相同的类型。
您可以通过 明确指定 F
参数:
来解决这个问题
call_both::<fn(u32) -> bool>(foo, bar);
call_both::<fn(_) -> _>(foo, bar); // <-- even this works
指定类型后,编译器可以正确地将参数强制转换为函数指针。您还可以 as
-将第一个参数显式转换为 fn(u32) -> bool
。
您也可以通过显式声明函数指针类型来修复第二个示例:
let mut x: fn(u32) -> bool = foo;
x = bar;
通常:在某处指定函数指针类型以触发强制转换会起作用。
我有以下代码 (Playground):
// Two dummy functions, both with the signature `fn(u32) -> bool`
fn foo(x: u32) -> bool {
x % 2 == 0
}
fn bar(x: u32) -> bool {
x == 27
}
fn call_both<F>(a: F, b: F)
where
F: Fn(u32) -> bool,
{
a(5);
b(5);
}
fn main() {
call_both(foo, bar); // <-- error
}
对我来说,这似乎应该编译为 foo
和 bar
具有相同的签名:fn(u32) -> bool
。然而,我收到以下错误:
error[E0308]: mismatched types
--> src/main.rs:20:20
|
20 | call_both(foo, bar);
| ^^^ expected fn item, found a different fn item
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool {bar}`
使用此代码可以触发相同的错误:
let mut x = foo;
x = bar; // <-- error
我也试过再次将bar
转换为函数指针类型:
let mut x = foo;
x = bar as fn(u32) -> bool; // <-- error
这导致了一个稍微不同的错误:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | x = bar as fn(u32) -> bool;
| ^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool`
我完全不明白这些错误。 什么是 fn 项与 fn 指针,为什么 foo
和 bar
不同的 fn 项?
自 Rust PR #19891 合并以来,每个命名函数都有不同的类型。但是,您可以使用 as
运算符将函数转换为相应的函数指针类型。
call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);
仅转换第一个函数也是有效的:将在第二个函数上推断转换,因为两个函数必须具有相同的类型。
call_both(foo as fn(u32) -> bool, bar);
函数项与函数指针
当您通过名称引用函数时,您获得的类型不是函数指针(例如 fn(u32) -> bool
)。相反,您会得到函数的 项类型 的零大小值(例如 fn(u32) -> bool {foo}
)。
该值大小为零,因为它不存储实际的函数指针。类型完美地标识了函数,因此无需在类型中存储实际数据。这有几个优点,主要是关于更容易优化。函数指针就像您对其他语言的期望一样:它存储函数的地址。
一个函数指针通过存储地址指向函数; 函数项通过类型信息引用函数。
在许多情况下可以将函数项强制转换为函数指针,例如:作为函数的参数和在 let _: fn(u32) -> bool = foo;
语句中。此外,您可以将函数项显式转换为函数指针:foo as fn(u32) -> bool
.
您可以在 function items, function pointers and coercion 上的参考资料中阅读有关此主题的更多信息。
解决您的问题
在您的情况下,编译器不够聪明,无法确定您想要 foo
和 bar
中的函数指针而不是函数项。当您调用 call_both(foo, bar)
时,编译器将泛型类型 F
设置为 fn(u32) -> bool {foo}
,因为这是第一个参数的类型。然后它抱怨第二个参数没有相同的类型。
您可以通过 明确指定 F
参数:
call_both::<fn(u32) -> bool>(foo, bar);
call_both::<fn(_) -> _>(foo, bar); // <-- even this works
指定类型后,编译器可以正确地将参数强制转换为函数指针。您还可以 as
-将第一个参数显式转换为 fn(u32) -> bool
。
您也可以通过显式声明函数指针类型来修复第二个示例:
let mut x: fn(u32) -> bool = foo;
x = bar;
通常:在某处指定函数指针类型以触发强制转换会起作用。