4

I have this C code:

typedef void (*f_t)(int a);

struct Foo {
        f_t f;
};

extern void f(struct Foo *);

bindgen generates the following Rust code (I have removed unimportant details):

#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct Foo {
    pub f: ::std::option::Option<extern "C" fn(a: ::std::os::raw::c_int)>,
}

I do not understand why Option is here. Obviously that Rust enum and C pointer are not the same thing on the bit level, so how does the Rust compiler handle this?

When I call the C f function and pass a pointer to a Rust struct Foo, does the compiler convert Foo_rust to Foo_C and then only pass a pointer to Foo_C to f?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user1244932
  • 7,352
  • 5
  • 46
  • 103

1 Answers1

7

From The Rust Programming Language chapter on FFI (emphasis mine):

Certain types are defined to not be null. This includes references (&T, &mut T), boxes (Box<T>), and function pointers (extern "abi" fn()). When interfacing with C, pointers that might be null are often used. As a special case, a generic enum that contains exactly two variants, one of which contains no data and the other containing a single field, is eligible for the "nullable pointer optimization". When such an enum is instantiated with one of the non-nullable types, it is represented as a single pointer, and the non-data variant is represented as the null pointer. So Option<extern "C" fn(c_int) -> c_int> is how one represents a nullable function pointer using the C ABI.

Said another way:

Obviously that Rust enum and C pointer are not the same thing on the bit level

They actually are, when the Option contains a specific set of types.


See also:

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366