我了解大写字母F的三个trait: Fn
, FnMut
, FnOnce
。我知道它们是trait并且像trait一样工作。
但是小写字母f的fn
呢?编辑器对它进行了不同的着色,这告诉我它不是一个trait。它还可以在某些其他trait无法使用的地方使用(反之亦然),虽然在其他情况下似乎表现类似。我在文档中找不到直接解释它的任何内容。
fn foo() {...}
创建函数时,你得到的是函数项(Function items)。它还是元组结构体或枚举变体的构造函数的类型。函数项大小为零(不包含数据),每个非泛型函数都有一个独特的、无法命名的函数项类型。在错误消息中,编译器将这些"伏地魔类型(Voldemort types)"显示为类似于fn() -> () {foo}
(函数名称在{}
中)的形式。|args| expression
)来创建一个闭包。与函数项一样,闭包也有独特的、无法命名的类型(编译器呈现的形式类似于[closure@src/main.rs:4:11: 4:23]
)。函数指针(Function pointers) 就是您所询问的类型,它们看起来像 fn() -> ()
。函数指针不能包含数据,但它们不是零大小;正如其名称所示,它们是指针。函数指针可以指向函数项或未捕获任何内容的闭包,但不能为 null。
当可能时,函数项和闭包会自动转换为相关的函数指针类型,因此 let f: fn(i32) = |_| ();
可以工作:因为闭包未捕获任何内容,所以它可以强制转换为函数指针。
所有三种类似函数的类型都实现了相关的 Fn
、FnMut
和 FnOnce
特性(除了闭包可能不实现 Fn
或 FnMut
,这取决于它们捕获了什么)。函数项和函数指针也实现了 Copy
、Clone
、Send
和 Sync
(只有当闭包的全部内容都实现这些特性时,闭包才会实现这些特性)。
dyn Fn
trait 对象快,因为后者还需要进行虚表查找以及间接调用。然而,在实际代码中有许多变量会使得浅显的分析变得复杂;如果性能差异对你很重要,你应该测量它而不是猜测哪个更快。
fn
。不过,这可能会隐式地禁止它捕获任何东西。 - brundolf