这个程序实际上根本没有构造函数指针——它总是调用foo
和这两个闭包直接地。
每个 Rust 函数,无论是闭包还是fn
item,具有独特的匿名类型。该类型实现了Fn
/FnMut
/FnOnce
特征,视情况而定。 a 的匿名类型fn
item 的大小为零,就像没有捕获的闭包类型一样。
因此,表达式create(foo)
实例化create
的参数F
with foo
的类型 - 这不是函数指针类型fn()
,但只是一个匿名的、零大小的类型foo
。在错误消息中,rustc 调用此类型fn() {foo}
, 如你看到的.
Inside create::<fn() {foo}>
(使用错误消息中的名称),表达式caller::<F>()
将此类型转发到caller
而不给它该类型的值。
Finally, in caller::<fn() {foo}>
the expression closure()
desugars to FnMut::call_mut(closure)
. Because closure
has type &mut F
where F
is just the zero-sized type fn() {foo}
, the 0
value of closure
itself is simply never used1, and the program calls foo
directly.
同样的逻辑也适用于闭包|| println!("Okay...")
,其中喜欢foo
有一个匿名的零大小类型,这次称为类似的东西.
The second closure is not so lucky- its type is not zero-sized, because it must contain a reference to the variable val
. This time, FnMut::call_mut(closure)
actually needs to dereference closure
to do its job. So it crashes2.
1 Constructing a null reference like this is technically undefined behavior, so the compiler makes no promises about this program's overall behavior. However, replacing 0
with some other "address" with the alignment of F
avoids that problem for zero-sized types like fn() {foo}
, and gives !)
2 Again, constructing a null (or dangling) reference is the operation that actually takes the blame here- after that, anything goes. A segfault is just one possibility- a future version of rustc, or the same version when run on a slightly different program, might do something else entirely!