Here we describe how Crubit maps function pointer types.
C++ function pointer types map into the corresponding Rust types as follows:
| C++ | Rust |
|---|---|
std::type_identity_t<void(int32_t)> & | extern "C" fn(i32) |
std::type_identity_t<void(int32_t)> * | Option<extern "C" fn(i32)> |
std::type_identity_t<void(int32_t)> | Not supported |
The bindings support function pointers with non-"C" calling conventions that are supported by both clang and Rust - e.g. "fastcall", "vectorcall, "thiscall", or "stdcall".
The bindings fail for bindings that might require thunks (e.g. when parameter types or a return type require passing structs by value).
TODO(b/276461979): Reject function pointer types that pass structs by value.
When used as function parameter types or function return types, Rust function pointer types map into the corresponding C++ types as follows:
| Rust | C++ |
|---|---|
extern "C" fn(i32) | std::type_identity_t<void(int32_t)>& |
Option<extern "C" fn(i32)> | Not supported |
In other scenarios Rust function pointer types map into the corresponding C++ types as follows:
| Rust | C++ |
|---|---|
extern "C" fn(i32) | std::type_identity_t<void(int32_t)>* |
Option<extern "C" fn(i32)> | Not supported |
TODO: Link to the not-yet-written references.md with a longer explanation why Rust references are sometimes bound to C++ references and sometimes to C++ pointers (C++ references can only be bound once; C++ temporaries).
The examples above use std::type_identity_t (C++20) to provide a more convenient syntax. Crubit doesn't actually require using std::type_identity_t in the C++ APIs that it generates bindings for.
Note that C++ function pointers are nullable, but Rust function pointers are not. (C++ function references are a separate, non-nullable type.)
There is no way to specify the lifetime of a function pointer in Rust, nor using Crubit's lifetime annotations - both assume a 'static lifetime. In scenarios where the lifetime may be shorter than 'static (e.g. JIT compilation, or dynamic loading of plugins at runtime) the developer is responsible for managing the lifetime of the function pointer.
The default calling convention of a Rust functions and function pointers is not compatible with "C" ABI - directly using function pointers in FFI may require explicitly specifying the ABI as follows: extern "C" fn(i32, i32) -> i32.
C++ also has plain function types (that is, the type pointed to by function pointers). There is no Rust equivalent. However, since C++ functions implicitly coerce to function pointers, this only comes up in template classes like std::function or absl::AnyInvocable.
TODO: To support bindings of types like std::function, absl::AnyInvocable, etc., Crubit may eventually need to provide a way to represent function types in Rust using a custom marker type provided via crubit/support/cc_std.