doc: writing design doc

This commit is contained in:
2026-08-06 20:22:17 +08:00
parent eb6453362d
commit 0141c461da
3 changed files with 81 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
# 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 `<stddef.h>`. C++ supports `bool` natively
and does not need any header file.
- In C, the integer types are provided by `<stdint.h>`. In C++, they are provided
by `<cstdint>`.
- In C, the `uintptr_t` and `intptr_t` types are provided by `<stdint.h>`. In C++,
they are provided by `<cstdint>`.
- 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.
View File
View File