I'm tracking down an error in third party code and I narrowed it down to something along the lines of.
use libc::c_void;
pub unsafe fn foo() {}
fn main() {
let ptr = &foo as *const _ as *const c_void;
println!("{:x}", ptr as usize);
}
Ran on stable 1.38.0 this prints the function pointer, but beta (1.39.0-beta.6) and nightly return '1'. (Playground)
What is the _
getting inferred to and why has the behaviour changed?
I assume the correct way to cast this would simply be foo as *const c_void
, but this is not my code.
This answer is based on the replies on the bug report motivated by this question.
Each function in Rust has its individual function item type, which is distinct from the function item type of every other function. For this reason, an instance of the function item type does not need to store any information at all – what function it points to is clear from its type. So the variable x in
let x = foo;
is a variable of size 0.
Function item types implicitly coerce to function pointer types where necessary. The variable
let x: fn() = foo;
is a generic pointer to any function with signature fn()
, and thus needs to store a pointer to the function it actually points to, so the size of x
is the size of a pointer.
If you take the address of a function, &foo
, you are actually taking the address of a zero-sized temporary value. Before this commit to the rust
repo, zero-sized temporaries used to create an allocation on the stack, and &foo
returned the address of that allocation. Since this commit, zero-sized types don't create allocations anymore, and instead use the magic address 1. This explains the difference between the different versions of Rust.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With