# C-friendly FFI Design This document is a guide for authoring Rust libraries that expose a C-friendly FFI and distributing them as ordinary CMake/pkg-config packages. It covers how to design C-friendly FFI signatures, how to pass primitive types, enums, strings and Rust-specific constructs such as `Option` and `Result` across the boundary, and how to write the accompanying C/C++ header files. ## Allowed Data Types Under this section we discuss which data types are allowed to appear in an FFI interface, how to pass them across the boundary, and how to convert Rust data types into these allowed types. ### Primitive Type Conversion This subsection covers the conversion of primitive types. The rules described here apply to both parameter and return value positions. #### Types Allowed to Cross the Boundary Directly The following types are allowed to be passed directly across the FFI boundary. The left column lists the Rust type and the right column lists the corresponding C/C++ type. | Rust | C/C++ | |------|-------| | `bool` | `bool` | | `f32` | `float` | | `f64` | `double` | | `i8` | `int8_t` | | `i16` | `int16_t` | | `i32` | `int32_t` | | `i64` | `int64_t` | | `u8` | `uint8_t` | | `u16` | `uint16_t` | | `u32` | `uint32_t` | | `u64` | `uint64_t` | | `usize` | `uintptr_t` | | `isize` | `intptr_t` | #### Required Header Files in the Generated C/C++ Headers The generated C/C++ header files must include the appropriate header files for the types listed above. - In C, the `bool` type is provided by ``. C++ supports `bool` natively and does not need any header file. - In C, the integer types are provided by ``. In C++, they are provided by ``. - In C, the `uintptr_t` and `intptr_t` types are provided by ``. In C++, they are provided by ``. - The floating-point types do not require any header file. #### The u128 and i128 Problem The `u128` and `i128` types have very poor support in the C/C++ standard libraries. Therefore they cannot be passed directly across the FFI boundary. To transfer data of these types, refer to the later section about passing opaque structs. #### The char Problem In C/C++, the type closest to the Rust `char` type is `char32_t`. However, the Rust `char` type only holds valid Unicode scalar values, which conflicts with `char32_t` being any 32-bit unsigned integer. In addition, the Rust official documentation explicitly states that the `char` type does not have FFI safety, so it cannot be used as a value passed across the FFI boundary. The solution is to use the Rust `u32` type for passing. Under this scheme: - On the Rust side: - The function signature uses `u32` as the parameter type. - For input parameters, use `char::try_from` to perform a fallible conversion that validates the incoming character. - For output parameters, use `u32::from` for the conversion. - On the C/C++ header side: - The function signature uses `char32_t` as the parameter type.