Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a function reference producing an invalid pointer?

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.

like image 648
Maciej Goszczycki Avatar asked Oct 16 '19 15:10

Maciej Goszczycki


1 Answers

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.

like image 128
Sven Marnach Avatar answered Sep 29 '22 23:09

Sven Marnach