强制转换产生无效指针的函数引用?

Casting a function reference producing an invalid pointer?

我正在追踪第三方代码中的一个错误,并将其缩小到类似的范围内。

use libc::c_void;

pub unsafe fn foo() {}

fn main() {
    let ptr = &foo as *const _ as *const c_void;
    println!("{:x}", ptr as usize);
}

运行 在稳定版 1.38.0 上打印函数指针,但 beta (1.39.0-beta.6) 和 nightly return '1'。 (Playground)

_ 被推断为什么以及为什么行为发生了变化?

我认为正确的转换方式应该是 foo as *const c_void,但这不是我的代码。

What is the _ getting inferred to and why has the behaviour changed?

每次进行原始指针转换时,您只能更改一条信息(引用或原始指针;可变性;类型)。因此,如果您执行此转换:

let ptr = &foo as *const _

因为您已经从引用更改为原始指针,为 _ 推断的类型必须 保持不变,因此是 [=14= 的类型], 这是函数 foo.

的一些无法表达的类型

您可以直接转换为函数指针,而不是那样做,这在 Rust 语法中是可以表达的:

let ptr = foo as *const fn() as *const c_void;

至于为什么变了,就不好说了。这可能是夜间构建中的错误。值得 reporting it — 即使它不是错误,您也可能会从编译器团队那里得到关于实际发生的事情的很好解释!

此答案基于 the bug report motivated by this question.

上的回复

Rust 中的每个函数都有其单独的函数项类型,这与其他所有函数的函数项类型不同。出于这个原因,函数项类型的实例根本不需要存储任何信息——它指向的函数从它的类型中就很清楚了。所以

中的变量 x
let x = foo;

是一个大小为 0 的变量。

函数项类型在必要时隐式强制转换为函数指针类型。变量

let x: fn() = foo;

是指向任何函数的通用指针,签名为fn(),因此需要存储指向它实际指向的函数的指针,因此[的大小=13=]是一个指针的大小。

如果您取一个函数的地址,&foo,您实际上取的是一个零大小的临时值的地址。在 this commit to the rust repo 之前,零大小的临时对象用于在堆栈上创建分配,而 &foo 返回该分配的地址。自此提交以来,零大小类型不再创建分配,而是使用魔术地址 1。这解释了不同版本的 Rust 之间的差异。