在 Rust 中,什么是 fn() -> () ?
In Rust, what is `fn() -> ()`?
我掌握了Fn(大写-F)的性状:Fn
、FnMut
、FnOnce
。我明白它们是特质,并且像特质一样工作。
但是 fn
(小写-f)呢?它在编辑器中得到了不同的颜色,这告诉我这不是一个特征。它也可以用于某些其他方法不能使用的地方(反之亦然),尽管它在其他情况下的表现似乎相似。我在文档中找不到任何直接解释它的内容。
它仅指向函数,而不是闭包,因为它只包含函数的地址,而不是闭包所需的捕获环境。
A Fn
特征(大写 F)可以指闭包或函数。
fn 是函数指针的类型。另请参阅文档中的此处:
https://doc.rust-lang.org/std/primitive.fn.html
Rust 有三种 function-like 类型:
Function items 是您使用 fn foo() {...}
创建函数时得到的结果。它也是 tuple-like 结构或枚举变体的构造函数的类型。函数项是 zero-sized(它们不包含数据),每个 non-generic 函数都有一个唯一的、不可命名的函数项类型。在错误消息中,编译器将这些“伏地魔类型”显示为类似 fn() -> () {foo}
(函数名称在 {}
中)。
Closures 是类似于函数项的值,但闭包可能包含数据:它们从环境中捕获的任何变量的副本或引用。如您所知,您可以使用闭包语法 (|args| expression
) 创建闭包。与函数项一样,闭包具有唯一的、不可命名的类型(由编译器呈现为 [closure@src/main.rs:4:11: 4:23]
)。
Function pointers 是您要问的:看起来像 fn() -> ()
的类型。函数指针不能包含数据,但它们不是zero-sized;顾名思义,它们是指针。一个函数指针可以指向一个函数项,或者指向一个不捕获任何东西的闭包,但它不能为空。
函数项和闭包会在可能的情况下自动强制转换为相关的函数指针类型,这就是 let f: fn(i32) = |_| ();
起作用的原因:因为闭包不捕获任何内容,所以它可以被强制转换为函数指针。
所有三种 function-like 类型都实现了相关的 Fn
、FnMut
和 FnOnce
特征(除了闭包可能不会实现 Fn
或 FnMut
取决于他们捕获的内容)。函数项和函数指针还实现了 Copy
、Clone
、Send
和 Sync
(闭包仅在其所有内容都实现时才实现这些特征)。
Performance-wise,函数指针是泛型和特征对象之间的某种折衷。它们必须被取消引用才能被调用,因此调用函数指针可能比直接调用函数项或闭包慢,但仍然比调用 dyn Fn
trait 对象快,后者除了间接查找外还涉及 vtable 查找称呼。然而,在实际代码中有许多变量会混淆朴素的分析;如果性能差异对您很重要,您应该对其进行测量,而不是猜测哪个更快。
参考资料
我掌握了Fn(大写-F)的性状:Fn
、FnMut
、FnOnce
。我明白它们是特质,并且像特质一样工作。
但是 fn
(小写-f)呢?它在编辑器中得到了不同的颜色,这告诉我这不是一个特征。它也可以用于某些其他方法不能使用的地方(反之亦然),尽管它在其他情况下的表现似乎相似。我在文档中找不到任何直接解释它的内容。
它仅指向函数,而不是闭包,因为它只包含函数的地址,而不是闭包所需的捕获环境。
A Fn
特征(大写 F)可以指闭包或函数。
fn 是函数指针的类型。另请参阅文档中的此处: https://doc.rust-lang.org/std/primitive.fn.html
Rust 有三种 function-like 类型:
Function items 是您使用
fn foo() {...}
创建函数时得到的结果。它也是 tuple-like 结构或枚举变体的构造函数的类型。函数项是 zero-sized(它们不包含数据),每个 non-generic 函数都有一个唯一的、不可命名的函数项类型。在错误消息中,编译器将这些“伏地魔类型”显示为类似fn() -> () {foo}
(函数名称在{}
中)。Closures 是类似于函数项的值,但闭包可能包含数据:它们从环境中捕获的任何变量的副本或引用。如您所知,您可以使用闭包语法 (
|args| expression
) 创建闭包。与函数项一样,闭包具有唯一的、不可命名的类型(由编译器呈现为[closure@src/main.rs:4:11: 4:23]
)。Function pointers 是您要问的:看起来像
fn() -> ()
的类型。函数指针不能包含数据,但它们不是zero-sized;顾名思义,它们是指针。一个函数指针可以指向一个函数项,或者指向一个不捕获任何东西的闭包,但它不能为空。
函数项和闭包会在可能的情况下自动强制转换为相关的函数指针类型,这就是 let f: fn(i32) = |_| ();
起作用的原因:因为闭包不捕获任何内容,所以它可以被强制转换为函数指针。
所有三种 function-like 类型都实现了相关的 Fn
、FnMut
和 FnOnce
特征(除了闭包可能不会实现 Fn
或 FnMut
取决于他们捕获的内容)。函数项和函数指针还实现了 Copy
、Clone
、Send
和 Sync
(闭包仅在其所有内容都实现时才实现这些特征)。
Performance-wise,函数指针是泛型和特征对象之间的某种折衷。它们必须被取消引用才能被调用,因此调用函数指针可能比直接调用函数项或闭包慢,但仍然比调用 dyn Fn
trait 对象快,后者除了间接查找外还涉及 vtable 查找称呼。然而,在实际代码中有许多变量会混淆朴素的分析;如果性能差异对您很重要,您应该对其进行测量,而不是猜测哪个更快。