FFI Interface
The FFI (Foreign Function Interface) layer enables communication between the Rust core (chameleon-core) and Go runtime (chameleon) via a stable C ABI. This boundary handles schema parsing, validation, SQL generation, and memory management across language boundaries.
Overview
The FFI bridge provides:- Schema parsing -
.cham→ JSON AST - Schema validation - Type checking with structured errors
- SQL generation - Queries and migrations
- Memory management - Safe allocation/deallocation across boundaries
- Version checking - Ensure Rust/Go compatibility
Architecture
- Go calls C function via
cgo - C function marshals to Rust (UTF-8 strings)
- Rust processes and returns JSON (serialized via
serde_json) - C function returns pointer to Go
- Go deserializes JSON and frees Rust memory
C ABI Functions
Location:chameleon-core/src/ffi/mod.rs
Parse Schema
input- Null-terminated.chamsource codeerror_out- Output pointer for error JSON (if any)
- JSON AST on success (caller must free with
chameleon_free_string) NULLon failure (error_outcontains error JSON)
Validate Schema
input- Null-terminated.chamsource codeerror_out- Output pointer for validation result JSON
ChameleonResult::Ok- Schema is validChameleonResult::ParseError- Syntax errorChameleonResult::ValidationError- Type check failedChameleonResult::InternalError- Unexpected error
Generate SQL
query_json- Query specification (JSON)schema_json- Schema AST (JSON)error_out- Output pointer for generated SQL (JSON)
ChameleonResult::Ok- SQL generated (error_outcontains result)ChameleonResult::ValidationError- Invalid queryChameleonResult::InternalError- Unexpected error
Generate Migration
schema_json- Schema AST (JSON)error_out- Output pointer for migration SQL
ChameleonResult::Ok- Migration SQL inerror_outChameleonResult::ValidationError- Invalid schemaChameleonResult::InternalError- Unexpected error
Free String
s- String allocated by Rust (from any FFI function)
- Safe to call with
NULL(no-op) - Must be called for every non-NULL pointer returned by Rust
- Never call twice on the same pointer (double-free)
Version
- Static string with Rust core version (e.g., “0.1.0-beta”)
- Never needs to be freed (static lifetime)
Result Codes
Go Bindings
Location:chameleon/internal/ffi/bindings.go
CGO Setup
-L/usr/local/lib- Library search path-Wl,-rpath,/usr/local/lib- Runtime library path (embeds path in binary)-lchameleon- Link againstlibchameleon.so(Linux) orlibchameleon.dylib(macOS)
ParseSchema
C.CString(input)- Allocate C string (must free withC.free)C.chameleon_parse_schema()- Call Rust (returns Rust-allocated string)C.GoString(cResult)- Copy to Go stringC.chameleon_free_string(cResult)- Free Rust allocation
ValidateSchemaRaw
Version
Building libchameleon.so
Prerequisites
Build Steps
Install System-Wide
Linux:Verify Installation
Cargo Configuration
Location:chameleon-core/Cargo.toml
rlib- Rust static library (for Rust consumers)staticlib- C-compatible static library (.a)cdylib- C-compatible dynamic library (.so,.dylib,.dll)
C Header Generation
Location:chameleon-core/build.rs:22
include/chameleon.h):
Linking from Go
Development Build
Production Build
Docker Build
Memory Management
Ownership Rules
| Allocator | Deallocator | Example |
|---|---|---|
Go (C.CString) | Go (C.free) | Input strings to Rust |
Rust (CString::into_raw) | Rust (chameleon_free_string) | Return values from Rust |
| Rust (static) | Never | chameleon_version() |
Common Pitfalls
❌ Double-free:Performance Characteristics
| Operation | Overhead | Notes |
|---|---|---|
| FFI call | ~100ns | Function call + argument marshaling |
| String copy (Go → C) | ~50ns/KB | C.CString() allocation |
| String copy (C → Go) | ~50ns/KB | C.GoString() allocation |
| JSON serialization | ~1μs/entity | serde_json::to_string() |
| JSON deserialization | ~2μs/entity | json.Unmarshal() in Go |
- Small schema (5 entities): ~500μs
- Medium schema (20 entities): ~2ms
- Large schema (100 entities): ~10ms
- FFI overhead: +30% (but Rust parser is 3x faster)
- Net result: 2x faster than pure Go
Testing
Location:chameleon-core/src/ffi/mod.rs:471
Rust Tests
Go Tests
Location:chameleon/internal/ffi/bindings_test.go (hypothetical)
Troubleshooting
Library Not Found
Error:Symbol Not Found
Error:- Rust library not built with
cdylibcrate type - Function not marked
#[no_mangle] - Header out of sync with implementation
Version Mismatch
Error:See Also
- Parser and AST - Rust parser implementation
- Type Checker - Validation before FFI export
- cbindgen Documentation - C header generation
- cgo Documentation - Go C interop