在 Rust 中,`fn() -> ()` 是什么?

9

我了解大写字母F的三个trait: Fn, FnMut, FnOnce。我知道它们是trait并且像trait一样工作。

但是小写字母f的fn呢?编辑器对它进行了不同的着色,这告诉我它不是一个trait。它还可以在某些其他trait无法使用的地方使用(反之亦然),虽然在其他情况下似乎表现类似。我在文档中找不到直接解释它的任何内容。

3个回答

23
Rust有三种类似函数的类型:
  1. 当你使用fn foo() {...}创建函数时,你得到的是函数项(Function items)。它还是元组结构体或枚举变体的构造函数的类型。函数项大小为零(不包含数据),每个非泛型函数都有一个独特的、无法命名的函数项类型。在错误消息中,编译器将这些"伏地魔类型(Voldemort types)"显示为类似于fn() -> () {foo}(函数名称在{}中)的形式。
  2. 闭包(Closures)是类似于函数项的值,但闭包可以包含数据:它们从它们的环境中捕获的变量的副本或引用。如你所知,你可以使用闭包语法(|args| expression)来创建一个闭包。与函数项一样,闭包也有独特的、无法命名的类型(编译器呈现的形式类似于[closure@src/main.rs:4:11: 4:23])。
  • 函数指针(Function pointers) 就是您所询问的类型,它们看起来像 fn() -> ()。函数指针不能包含数据,但它们不是零大小;正如其名称所示,它们是指针。函数指针可以指向函数项或未捕获任何内容的闭包,但不能为 null。

  • 当可能时,函数项和闭包会自动转换为相关的函数指针类型,因此 let f: fn(i32) = |_| (); 可以工作:因为闭包未捕获任何内容,所以它可以强制转换为函数指针。

    所有三种类似函数的类型都实现了相关的 FnFnMutFnOnce 特性(除了闭包可能不实现 FnFnMut,这取决于它们捕获了什么)。函数项和函数指针也实现了 CopyCloneSendSync(只有当闭包的全部内容都实现这些特性时,闭包才会实现这些特性)。

    从性能上来看,函数指针在泛型和特质对象之间是一种妥协。它们必须被取消引用才能被调用,因此调用函数指针可能比直接调用函数项或闭包要慢,但仍然比调用 dyn Fn trait 对象快,因为后者还需要进行虚表查找以及间接调用。然而,在实际代码中有许多变量会使得浅显的分析变得复杂;如果性能差异对你很重要,你应该测量它而不是猜测哪个更快。
    参考资料:

    4

    这是一个函数指针类型

    它仅引用函数,而不是闭包,因为它只包含函数的地址,而不是闭包所需的捕获环境。

    Fn trait(大写F)既可以引用闭包也可以引用函数。


    我能将闭包转换为 fn。不过,这可能会隐式地禁止它捕获任何东西。 - brundolf
    1
    @brundolf 你猜对了,一个不捕获任何东西的闭包实际上就是一个函数。 - prog-fh

    1

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接