Expand description
§cimpl - Simple C implementations from Rust
Create clean, safe C FFI bindings that AI can automatically convert to any language.
§The Vision
Rust + cimpl → Clean C API → AI-powered bindings → All languagesWrite your library once in safe Rust, expose it through a clean C API using cimpl’s macros, and let AI generate high-quality bindings for Python, JavaScript, Lua, Ruby, C#, Java, Go, and more.
§Why cimpl?
Most Rust FFI examples show trivial code. Real FFI is much harder:
- How do you return complex types like strings or structs?
- How do you propagate
Result<T, E>errors across the FFI boundary? - How do you handle object lifecycle (constructors, methods, destructors)?
- How do you prevent memory leaks and double-frees?
- How do you make errors usable in other languages?
cimpl solves the hard problems:
- ✅ Type-safe pointer tracking with validation
- ✅ Automatic error handling with descriptive, parseable messages
- ✅ Memory leak detection in tests
- ✅ Clean macros for production patterns (not toy examples)
- ✅ Object-oriented APIs (structs with methods, not just functions)
- ✅ AI-friendly C headers (auto-generated via cbindgen)
- ✅ One codebase → many language bindings
§Quick Example
use cimpl::*;
use std::ffi::c_void;
use std::os::raw::c_char;
use thiserror::Error as ThisError;
// Your library's error type (using thiserror for convenience)
#[derive(ThisError, Debug)]
pub enum Error {
#[error("value out of range: {0}")]
OutOfRange(String),
#[error("invalid UTF-8: {0}")]
InvalidUtf8(String),
}
// Map to cimpl::Error for FFI - one line with from_error()!
impl From<Error> for cimpl::Error {
fn from(e: Error) -> Self {
cimpl::Error::from_error(e) // Automatic: Debug → variant, Display → message
}
}
// Clean, safe FFI function
#[no_mangle]
pub extern "C" fn process_value(ptr: *mut MyType) -> *mut c_char {
let obj = deref_or_return_null!(ptr, MyType);
let result = ok_or_return_null!(obj.to_string()); // Error → cimpl::Error automatically
to_c_string(result)
}
// Memory management wrapper (required for namespace safety)
#[no_mangle]
pub extern "C" fn my_free(ptr: *mut c_void) -> i32 {
cimpl::cimpl_free(ptr) // Rust-level function, wrap in your C API
}That’s it! From this simple code:
- cbindgen generates a C header with proper namespace prefix
- Type validation ensures safety
- Errors map to descriptive strings:
"VariantName: details" - Memory is tracked automatically
- AI can generate bindings for any language
§Core Features
§String-Based Error Handling
Errors use a consistent "VariantName: details" format that works across all languages:
use cimpl::Error;
// Create errors manually
let err = Error::new("OutOfRange", "value must be between 0 and 100");
// Or convert automatically from any std::error::Error
let err = Error::from_error(my_error); // Uses Debug for variant, Display for messageThis format is:
- ✅ Human-readable (developers)
- ✅ Machine-parseable (error handlers)
- ✅ AI-friendly (code generation)
- ✅ Cross-language (works in C, Python, Ruby, Swift, Go…)
§Pointer Safety Macros
box_tracked!- Allocate and track Boxptr_or_return!- Null pointer checks with automatic error messagesderef_or_return!- Pointer validation and dereferencing (immutable)deref_mut_or_return!- Pointer validation and dereferencing (mutable)
§String Conversion
cstr_or_return!- C string to Rust with UTF-8 validation and bounds checkingto_c_string()- Rust String to tracked C stringoption_to_c_string!- Optionto C string (NULL if None)
§Byte Array Handling
bytes_or_return!- Validate and convert C byte arraysto_c_bytes()- Rust Vecto tracked C byte array
§Result Handling
ok_or_return!- Result unwrapping with automatic error conversionok_or_return_null!- Unwrap Result, return NULL on errorok_or_return_false!- Unwrap Result, return false on error- Works with any error type implementing
Into<cimpl::Error>
§Option Handling
some_or_return!- Option unwrapping with custom errorssome_or_return_other_null!- Option with Error::other message, return NULL
§Memory Management
All pointers allocated via box_tracked!, arc_tracked!, or the tracking functions are
registered in a global, thread-safe registry. Each library should wrap cimpl_free():
#[no_mangle]
pub extern "C" fn mylib_free(ptr: *mut c_void) -> i32 {
cimpl::cimpl_free(ptr)
}This provides:
- Namespace safety: No symbol conflicts when linking multiple libraries
- Type validation: Wrong type returns error instead of crashing
- Double-free protection: Registry prevents freeing the same pointer twice
- Leak detection: Unfreed pointers reported at program exit
§AI-Friendly Design
cimpl is designed to enable AI code generation. See AI_WORKFLOW.md in the repository for:
- Pre-flight checklist for catching anti-patterns
- Complete macro reference with decision trees
- Common mistakes to avoid
- Step-by-step guidance for generating FFI code
Proven: The UUID example was generated entirely by AI in 15 minutes with zero errors.
§Examples
The crate includes two complete, production-ready examples:
-
examples/reference/- ValueConverter showing all FFI patterns- Clean lib.rs/ffi.rs separation
- Full Python bindings with ctypes
- Demonstrates struct methods, constructors, error handling
-
examples/uuid/- Real-world external crate wrapping- 15 FFI functions exposing uuid crate
- Complete Python bindings
- AI-generated in 15 minutes from documentation
- Demonstrates direct external crate usage pattern
§Philosophy
See PHILOSOPHY.md in the repository for the complete design rationale. Key insights:
- The C ABI is timeless - Build on solid ground
- Safety through validation - Not through complexity
- Standard C conventions - Developers know what to expect
- Universal patterns - One way to do things
- AI-friendly design - Enable code generation
- Language independence - Your C API outlives any specific tooling
Build once in Rust. Expose through C. Use everywhere.
Re-exports§
pub use error::Error;pub use error::Result;pub use error::Error as CimplError;pub use utils::cimpl_free;pub use utils::safe_slice_from_raw_parts;pub use utils::to_c_bytes;pub use utils::to_c_string;pub use utils::track_arc;pub use utils::track_arc_mutex;pub use utils::track_box;
Modules§
Macros§
- arc_
tracked - Create an Arc-wrapped pointer and track it Returns the raw pointer
- box_
tracked - Create a Box-wrapped pointer and track it Returns the raw pointer
- bytes_
or_ return - Validate and convert raw C byte array to safe slice, returning early on error.
- bytes_
or_ return_ int - Validate and convert raw C byte array to safe slice, return -1 on error.
- bytes_
or_ return_ null - Validate and convert raw C byte array to safe slice, return NULL on error.
- cimpl_
free - Free a pointer that was allocated by cimpl.
- cstr_
option - cstr_
or_ return - Convert C string with bounded length check or early-return with error value Uses a safe bounded approach to prevent reading unbounded memory. Maximum string length is MAX_CSTRING_LEN (1MB).
- cstr_
or_ return_ int - cstr_
or_ return_ null - If the expression is null, set the last error and return std::ptr::null_mut().
- cstr_
or_ return_ with_ limit - Convert C string with custom length limit or early-return with error value Allows specifying a custom maximum length for the string.
- deref_
mut_ or_ return - Validate pointer and dereference mutably, returning reference Returns early with custom value on error
- deref_
mut_ or_ return_ int - Validate pointer and dereference mutably, returning reference Returns -1 on error
- deref_
mut_ or_ return_ null - Validate pointer and dereference mutably, returning reference Returns NULL on error
- deref_
or_ return - Validate pointer and dereference immutably, returning reference Returns early with custom value on error
- deref_
or_ return_ false - Validate pointer and dereference immutably, returning reference Returns false on error
- deref_
or_ return_ int - Validate pointer and dereference immutably, returning reference Returns -1 on error
- deref_
or_ return_ null - Validate pointer and dereference immutably, returning reference Returns NULL on error
- deref_
or_ return_ zero - Validate pointer and dereference immutably, returning reference Returns 0 on error
- ok_
or_ return - Handle Result or early-return with error value
- ok_
or_ return_ false - Handle Result, early-return with false on error
- ok_
or_ return_ int - Handle Result, early-return with -1 (negative) on error
- ok_
or_ return_ null - Handle Result, early-return with null on error
- ok_
or_ return_ zero - Handle Result, early-return with 0 on error
- option_
to_ c_ string - Converts an
Option<String>to a C string pointer. Returnsnull_mut()if the Option is None. - ptr_
or_ return - Check pointer not null or early-return with error value
- ptr_
or_ return_ int - If the expression is null, set the last error and return -1.
- ptr_
or_ return_ null - If the expression is null, set the last error and return null.
- some_
or_ return - Handle Option, early-return with custom value if None
- some_
or_ return_ false - Handle Option, early-return with false if None
- some_
or_ return_ int - Handle Option, early-return with -1 if None
- some_
or_ return_ null - Handle Option, early-return with NULL if None
- some_
or_ return_ other_ false - Convenience macro: Handle Option with Error::other message, return false
- some_
or_ return_ other_ int - Convenience macro: Handle Option with Error::other message, return -1
- some_
or_ return_ other_ null - Convenience macro: Handle Option with Error::other message
- some_
or_ return_ other_ zero - Convenience macro: Handle Option with Error::other message, return 0
- some_
or_ return_ zero - Handle Option, early-return with 0 if None