Simple C implementations from Rust
Create clean, safe C FFI bindings for Rust that AI can automatically convert to any language.
Note: This is a new project (v0.3.0+) taking over the
cimplcrate name. Previous versions (0.1.x, 0.2.0) were a different, unrelated project and have been yanked.
Rust + cimpl → Clean C API → AI-powered bindings → All languages
Write your library once in safe Rust, expose it through a clean C API, and let AI generate high-quality bindings for Python, JavaScript, Lua, Ruby, C#, Java, Go, and more.
There is no special binding language and you don't have to annotate your code. This is important when you need to support more than one language.
Most Rust FFI examples are trivial examples:
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 { a + b }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 helps with 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
Note: For Node.js and WASM targets, use
wasm-bindgeninstead. While Node.js can use cimpl via Koffi FFI, WASM provides better performance and integration.
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 vc_to_string(value: *mut ValueConverter) -> *mut c_char {
let converter = deref_or_return_null!(value, ValueConverter);
let result = ok_or_return_null!(converter.to_string()); // Error → cimpl::Error automatically
to_c_string(result)
}
// Memory management wrapper (required for namespace safety)
#[no_mangle]
pub extern "C" fn vc_free(ptr: *mut c_void) -> i32 {
cimpl::cimpl_free(ptr) // Rust-level function, wrap in your C API
}That's it! From this 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
AI Users: See AI_WORKFLOW.md for a complete guide to generating FFI wrappers and language bindings with proper macro usage patterns.
#include "value_converter.h"
// Create from integer
ValueConverter* vc = vc_from_i32(42);
if (vc == NULL) {
char* msg = vc_last_error(); // "OutOfRange: need exactly 4 bytes..."
printf("Error: %s\n", msg);
vc_free(msg);
return -1;
}
// Convert to hex
char* hex = vc_to_hex(vc);
printf("Hex: %s\n", hex); // "2a000000"
// Cleanup (namespace-safe free function)
vc_free(hex);
vc_free(vc);from value_converter import ValueConverter, OutOfRangeError
try:
with ValueConverter.from_i32(42) as vc:
print(vc.to_hex()) # "2a000000"
except OutOfRangeError as e:
print(f"Error: {e}")- Tracked pointers with type validation
- Rust-level
cimpl::cimpl_free()function (wrap with{crate}_freein your C API) - Shared registry across all cimpl-based libraries
- Double-free protection
- Type mismatch detection
- Allocation tracking with double-free and type-mismatch detection
- String-based error messages with consistent
"VariantName: details"format - AI-friendly format: Easy to parse and convert to typed exceptions
- Automatic conversion via
cimpl::Error::from_error()or manual withcimpl::Error::new() - Works with thiserror: Use
#[derive(ThisError)]for ergonomic error definitions - Standard C conventions: NULL/false/-1 indicates error, call
*_last_error()for details
box_tracked!()- Allocate and track Boxptr_or_return!()- Null pointer checks with automatic error messagesbytes_or_return_*!()- Byte array validation with bounds checkingcstr_or_return_*!()- C string conversion with null checksderef_or_return_*!()- Pointer validation and dereferencingok_or_return_*!()- Result unwrapping with automatic error conversionoption_to_c_string!()- Option to C string (NULL if None)
Add to your Cargo.toml:
[dependencies]
cimpl = "0.4"
thiserror = "2.0" # Recommended for ergonomic errors
[build-dependencies]
cbindgen = "0.27"Read AI_WORKFLOW.md first! It contains:
- 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
examples/reference/ - Production-ready reference implementation
This is not a toy example. Unlike the typical add(a, b) FFI tutorials, this demonstrates the hard parts:
- Real-world utility: Value converter for type conversion (i32, u32, i64, u64, bytes, strings, hex)
- Multiple constructors:
from_i32(),from_string(),from_hex(), etc. - Fallible conversions:
to_i32()might fail (wrong size),to_string()might fail (invalid UTF-8) - Proper validation: Size limits, UTF-8 checks, overflow detection
- Memory safety: Tracked allocations, type validation
- Clear separation:
lib.rs(pure Rust API) vsffi.rs(C FFI wrapper)
Two-file structure:
src/lib.rs- Standard Rust library (no FFI concerns)src/ffi.rs- C FFI wrapper using cimpl
This shows how to add FFI to an existing Rust library, not how to write FFI from scratch.
See examples/reference/README.md for detailed API reference.
This is used in production for the C2PA project, providing C++, Python, Swift, Kotlin and other language bindings from a single Rust codebase.
Contributions welcome! This project is about making FFI simple and accessible.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.