"预期的fn项目,找到了不同的fn项目"使用函数指针时

时间:2015-01-12 05:41:50

标签: rust

我有以下代码(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
}

对我而言,似乎这应该编译成foobar具有相同的签名: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指针以及为什么foobar个不同的fn项目?

2 个答案:

答案 0 :(得分:11)

Rust PR #19891合并以来,每个命名函数都有一个不同的类型。但是,您可以使用as运算符将函数强制转换为相应的函数指针类型。

call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);

仅投射第一个函数也是有效的:在第二个函数中推断出强制转换,因为两个函数必须具有相同的类型。

call_both(foo as fn(u32) -> bool, bar);

答案 1 :(得分:4)

功能项与功能指针

使用函数名称引用函数时,得到的类型不是函数指针(例如fn(u32) -> bool)。取而代之的是,该函数的 item类型的大小为零(例如fn(u32) -> bool {foo})。

该值的大小为零,因为它不存储实际的函数指针。类型完美地标识了功能,因此不需要在类型中存储实际数据。这具有几个优点,主要是关于更容易的优化。函数指针就像您从其他语言中所期望的那样:它存储函数的地址。

功能指针通过存储的地址引用该功能; 功能项通过类型信息引用功能。

在许多情况下,函数项可以强制为函数指针,例如:作为函数的参数和let _: fn(u32) -> bool = foo;语句中的参数。此外,您可以将函数项显式转换为函数指针:foo as fn(u32) -> bool

您可以在function itemsfunction pointerscoercion的参考资料中了解有关此主题的更多信息。



解决问题的方法

在您的情况下,编译器不够聪明,无法确定您希望使用foobar中的函数指针,而不是函数项。当您调用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;

通常:在某个地方指定函数指针类型以触发强制将起作用