一键导入
rust-ffi
FFI cross-language interop expert covering C/C++ bindings, bindgen, cbindgen, PyO3, JNI, memory layout, data conversion, and safe FFI patterns.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
FFI cross-language interop expert covering C/C++ bindings, bindgen, cbindgen, PyO3, JNI, memory layout, data conversion, and safe FFI patterns.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Actor model expert covering message passing, state isolation, supervision trees, deadlock prevention, fault tolerance, Actix framework, and Erlang-style concurrency patterns.
Rust anti-patterns and common mistakes expert. Handles code review issues with clone abuse, unwrap in production, String misuse, index loops, and refactoring guidance.
Advanced async patterns expert covering Stream implementation, zero-copy buffers, tokio::spawn lifetimes, plugin system scheduling, tonic streaming, and async lifetime management.
Advanced async patterns expert. Handles Stream processing, backpressure control, select/join operations, cancellation, Future trait implementation, and async runtime optimization.
Authentication and authorization expert covering JWT, API keys, OAuth, RBAC, password hashing, distributed token storage, and session management patterns.
Caching and distributed storage expert covering Redis, connection pools, TTL strategies, cache patterns (Cache-Aside, Write-Through), invalidation, and performance optimization.
| name | rust-ffi |
| description | FFI cross-language interop expert covering C/C++ bindings, bindgen, cbindgen, PyO3, JNI, memory layout, data conversion, and safe FFI patterns. |
| metadata | {"triggers":["FFI","C interop","C++ interop","bindgen","cbindgen","PyO3","JNI","extern","libc","CString","CStr","cross-language"]} |
# Auto-generate bindings
bindgen input.h \
--output src/bindings.rs \
--allowlist-type 'my_*' \
--allowlist-function 'my_*'
# Generate C header
cbindgen --crate mylib --output include/mylib.h
use std::ffi::{CStr, CString};
use libc::c_int;
#[link(name = "curl")]
extern "C" {
fn curl_version() -> *const libc::c_char;
fn curl_easy_perform(curl: *mut c_int) -> c_int;
}
// ✅ Safe wrapper
fn get_version() -> String {
unsafe {
let ptr = curl_version();
// SAFETY: curl_version returns valid null-terminated string
CStr::from_ptr(ptr).to_string_lossy().into_owned()
}
}
// ✅ Safe way to pass strings
fn process_c_string(s: &CStr) {
// SAFETY: s is a valid CStr, ptr is valid for call duration
unsafe {
some_c_function(s.as_ptr());
}
}
// Creating CString from Rust
fn get_c_string() -> Result<CString, std::ffi::NulError> {
CString::new("hello")
}
// ❌ Dangerous: temporary CString
// let ptr = CString::new("hello").unwrap().as_ptr(); // Dangling!
// ✅ Correct: keep CString alive
let c_str = CString::new("hello")?;
let ptr = c_str.as_ptr();
// use ptr...
// c_str dropped here
extern "C" fn callback(data: *mut libc::c_void) {
// SAFETY: data must be a valid pointer to UserData
// Caller guarantees this invariant
unsafe {
let user_data: &mut UserData = &mut *(data as *mut UserData);
user_data.count += 1;
}
}
fn register_callback(callback: extern "C" fn(*mut c_void), data: *mut c_void) {
unsafe {
some_c_lib_register(callback, data);
}
}
// Using cxx for safe C++ FFI
use cxx::CxxString;
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("my_library.h");
type MyClass;
fn do_something(&self, input: i32) -> i32;
fn get_data(&self) -> &CxxString;
}
}
struct RustWrapper {
inner: cxx::UniquePtr<ffi::MyClass>,
}
impl RustWrapper {
pub fn new() -> Self {
Self {
inner: ffi::create_my_class(),
}
}
pub fn do_something(&self, input: i32) -> i32 {
self.inner.do_something(input)
}
}
| Rust | C | Notes |
|---|---|---|
i32 | int | Usually matches |
i64 | long long | Platform-dependent |
usize | uintptr_t | Pointer-sized |
*const T | const T* | Read-only |
*mut T | T* | Mutable |
&CStr | const char* | UTF-8 guaranteed |
CString | char* | Ownership transfer |
NonNull<T> | T* | Non-null pointer |
Option<NonNull<T>> | T* (nullable) | Nullable pointer |
fn call_c_api() -> Result<(), Box<dyn std::error::Error>> {
// SAFETY: c_function is properly initialized
let result = unsafe { c_function_that_returns_int() };
if result < 0 {
return Err(format!("C API error: {}", result).into());
}
Ok(())
}
// Panics across FFI boundary = UB
// Must catch or prevent
#[no_mangle]
pub extern "C" fn safe_call() -> i32 {
let result = std::panic::catch_unwind(|| {
rust_code_that_might_panic()
});
match result {
Ok(value) => value,
Err(_) => -1, // Error code
}
}
// C++ exceptions → Rust panic (with cxx)
// Must catch at FFI boundary
#[no_mangle]
pub extern "C" fn safe_cpp_call(error_code: *mut i32) -> *const c_char {
let result = std::panic::catch_unwind(|| {
unsafe { cpp_function() }
});
match result {
Ok(Ok(value)) => value.as_ptr(),
Ok(Err(e)) => {
if !error_code.is_null() {
unsafe { *error_code = e.code(); }
}
std::ptr::null()
}
Err(_) => {
if !error_code.is_null() {
unsafe { *error_code = -999; }
}
std::ptr::null()
}
}
}
| Scenario | Who Frees | How |
|---|---|---|
| C allocates, Rust uses | C | Don't free from Rust |
| Rust allocates, C uses | Rust | C notifies when done |
| Shared buffer | Agreed protocol | Document clearly |
// ✅ Rust allocates, C borrows
#[no_mangle]
pub extern "C" fn create_buffer(len: usize) -> *mut u8 {
let mut buf = vec![0u8; len];
let ptr = buf.as_mut_ptr();
std::mem::forget(buf); // Don't drop
ptr
}
#[no_mangle]
pub extern "C" fn free_buffer(ptr: *mut u8, len: usize) {
unsafe {
// SAFETY: ptr was allocated by create_buffer with this len
let _ = Vec::from_raw_parts(ptr, len, len);
} // Vec dropped, memory freed
}
Need to call C code?
→ Simple functions? Manual extern declarations
→ Complex API? Use bindgen
→ C++? Use cxx crate
Exporting to C?
→ Use cbindgen to generate headers
→ Mark functions #[no_mangle]
→ Use extern "C"
For every FFI call:
1. Document pointer validity requirements
2. Document lifetime expectations
3. Document thread safety assumptions
4. Document panic handling
unsafe FFI calls
↓
Safe private functions (validate inputs)
↓
Safe public API (no unsafe visible)
# Test with Miri
cargo +nightly miri test
# Memory safety check
valgrind ./target/release/program
# Cross-compile test
cargo build --target x86_64-unknown-linux-gnu
| Language | Tool | Use Case |
|---|---|---|
| Python | PyO3 | Python extensions |
| Java | jni | Android/JVM |
| Node.js | napi-rs | Node.js addons |
| C# | csharp-bindgen | .NET interop |
| Go | cgo | Go bridge |
| C++ | cxx | Safe C++ FFI |
| Pitfall | Consequence | Avoid By |
|---|---|---|
| String encoding error | Garbled text | Use CStr/CString |
| Lifetime mismatch | Use-after-free | Clear ownership |
| Cross-thread non-Send | Data race | Arc + Mutex |
| Fat pointer to C | Memory corruption | Flatten data |
| Missing #[no_mangle] | Symbol not found | Explicit export |
| Panic across FFI | UB | catch_unwind |
When reviewing FFI code:
# Check safety
cargo +nightly miri test
# Memory leaks
valgrind --leak-check=full ./target/release/program
# Generate bindings
bindgen wrapper.h --output src/ffi.rs
# Generate C header
cbindgen --lang c --output target/mylib.h
# Check exports
nm target/release/libmylib.so | grep my_function