Best practices to design a C API that can be ergonomically used in Rust
⚓ Rust 📅 2025-08-18 👤 surdeus 👁️ 12Hi,
For those of you who have worked on safe Rust bindings to C libraries before, what aspects of a C API makes it particularly suitable to be used in Rust.
Or in other words, what to do and what to avoid when designing a C API with the express purpose to be used within Rust.
Here are the things I came up with, but I suspect there are other things that I am forgetting.
General
- Obvious: It must be possible to build a safe abstraction.
- Keep things simple
- bindgen should have no issues creating Rust equivalent ffi-bindings.
- No C preprocessor Macros
- Support standard rust traits (where it makes sense):
- Debug, Clone, PartialEq, Eq, Default, Hash
All types on the FFI API surface should fall into one of the two categories:
1. Resource Manager Types
This kind of type manages (allocates and frees) a resource like memory or other system resources.
-
These types should should have straightforward object lifetimes and the C API should expose the following functions:
-
- function to create -> wrapped as
newmethod
- function to create -> wrapped as
-
- Functions to use functionality
-
- function to destroy -> wrapped as
dropmethod
- function to destroy -> wrapped as
-
-
Should be representable as a opaque types, only work on raw pointers (which are wrapped by safe abstraction)
2. Simple Value Type
- Only struct of value types
- Bitwise copyable
In your experience, what other parts should be considered, that are otherwise annoying (not ergonomic) or difficult to represent in Rust?
1 post - 1 participant
🏷️ Rust_feed