Numeric types
For numeric types to agree across an FFI, their kind (unsigned integer, signed integer, or floating point), size, and invariants must match. The size of most C/C++ types and usize
/isize
in Rust can vary depending on the platform. For all numeric types, if the size matches then the alignment will also match (on a single platform).
std::ffi defines type aliases for common numeric types which are platform-accurate; libc defines a few more aliases for less common types. Using these aliases is usually easier than using Rust types directly.
Integers, booleans, and characters
Rust integers
u8
... u64
and i8
... i64
are unsigned and signed respectively with the number in the type indicating the number of bits.
usize
and isize
are 32 bits on 32 bit platforms and 64 bits on 64 bit platforms.
C/C++ integers
A C/C++ integer is unsigned if it uses the unsigned
keyword and signed otherwise.
A char
is always 8 bits, a short
is always 16 bits, and a long long
is always 64 bits.
The size of int
and long
are platform dependent, see std::ffi::c_{int|long}_definition
128 bit integers
Rust supports i128
and u128
. These types are mostly not safe for FFI (will lead to UB) and must be avoided. In particular, they are not compatible with C's 128bit integer types where those exist. However, they can be used on non-Windows aarch64.
booleans
Rust (bool
) and C's (strictly, C99 and later, _Bool
) boolean types are compatible. Technically, C++'s bool
is not guaranteed to be the same representation as C's _Bool
, but they are on all known platforms, so it is safe to assume that Rust's bool
is compatible with C++'s.
It is common to use integers to represent booleans in C programs (especially older programs or when using older toolchains). These can be converted to Rust bool
s if the size matches and they are guaranteed to only have values 0
or 1
. (It is possible to use 0
for false and non-zero for true with C's boolean operators, however, storing any value other than 0
or 1
in a Rust bool
is UB. You can check and convert in either Rust or C code, but in the latter case you must not use a Rust bool
in your FFI).
characters
Rust and C character types are incompatible.
C character types can be converted to or from Rust's 8 bit integer types. unsigned char
is always u8
, signed char
is always i8
. char
may be either i8
or u8
depending on the platform, see std::ffi::c_char_definition.
A Rust char
is a 32 bit type which must be a valid Unicode scalar value. It is UB to create a char
which is not valid Unicode. You should probably avoid using char
in FFI unless you have a custom character type with the same size and invariant in your foreign code. Otherwise it is usually better to pass numeric bytes and use helper methods on char
to create the Rust char
.
TODO wchar_t
Non-zero integers
There are (currently unstable) type aliases for non-zero integers in core::ffi
. These map to the non-zero integer types in core::num
with the correct size for the C integer types. The user must maintain the non-zero invariant (whether that is a safety issue depends on how the types are used); i.e., Rust does not ensure that values with this type are in fact non-zero.
Floating point
A C float
is equivalent to a Rust f32
and a C double
is equivalent to a Rust f64
.
SIMD
SIMD vectors cannot be used in FFI (UB). There is an accepted RFC to address this, but it has not been implemented.