Skip to main content

Crate cimpl

Crate cimpl 

Source
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 languages

Write 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 message

This 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

§String Conversion

§Byte Array Handling

§Result Handling

§Option Handling

§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:

  1. The C ABI is timeless - Build on solid ground
  2. Safety through validation - Not through complexity
  3. Standard C conventions - Developers know what to expect
  4. Universal patterns - One way to do things
  5. AI-friendly design - Enable code generation
  6. 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§

error
macros
FFI Helper Macros
utils
FFI Utilities

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. Returns null_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