Rust Ffi

mohitmishra786/low-level-dev-skills/skills/rust/rust-ffi

作者 mohitmishra786bdc58472fa9f無授權條款253 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫3 個月前更新

Rust FFI skill for C interoperability. Use when calling C libraries from Rust, generating Rust bindings with bindgen, exporting Rust functions to C with cbindgen, writing safe wrappers around unsafe FFI, or linking system and vendor libraries. Activates on queries about bindgen, cbindgen, extern "C", unsafe FFI, Rust C bindings, linking C from Rust, or sys crates.

AI 產生的概覽

指導 Rust 與 C 的互通:bindgen 綁定、cbindgen 匯出、安全封裝、連結與 sys crate 結構。

功能
說明如何從 Rust 呼叫 C,包括手寫 extern "C" 宣告和透過 build.rs 使用 bindgen 產生綁定。涵蓋用 cbindgen 將 Rust 函式匯出給 C、為不安全 API 撰寫安全封裝、連結靜態函式庫、動態函式庫與框架,以及組織 sys crate 的結構。產出的是程式碼模式與設定範例,而非產生的檔案。
適用情境
適用於從 Rust 呼叫 C 函式庫、用 bindgen 產生 Rust 綁定、用 cbindgen 將 Rust 函式匯出給 C、為不安全 FFI 撰寫安全封裝,或連結系統函式庫與廠商函式庫的情境。也適用於設計 sys crate 或排查 build.rs 連結設定問題。
執行需求
不附帶指令稿,僅為說明性內容。需要 Rust 工具鏈與 Cargo,以及作為建置相依性的 bindgen 和 cbindgen(可選 pkg-config 與 cc crate),還需要 C 編譯器、bindgen 所需的 libclang,以及被綁定 C 標頭檔與函式庫的存取權限。

Rust FFI

Purpose

Guide agents through Rust's Foreign Function Interface: calling C from Rust with bindgen, exporting Rust to C with cbindgen, writing safe wrappers, linking libraries via build.rs, and structuring sys crates.

Triggers

  • "How do I call a C library from Rust?"
  • "How do I use bindgen to generate Rust bindings?"
  • "How do I export Rust functions to be called from C?"
  • "How do I write a safe wrapper around an unsafe C API?"
  • "How do I link a system library in Rust?"
  • "What is a sys crate and how do I structure one?"

Workflow

1. Calling C without bindgen (manual declarations)

rust
// Declare external C functions manuallyuse std::ffi::{c_int, c_char, c_void, CStr, CString};
extern "C" {    fn strlen(s: *const c_char) -> usize;    fn malloc(size: usize) -> *mut c_void;    fn free(ptr: *mut c_void);    fn my_lib_init(config: *const c_char) -> c_int;    fn my_lib_process(handle: *mut c_void, data: *const u8, len: usize) -> c_int;    fn my_lib_cleanup(handle: *mut c_void);}
// Call unsafe C function safelyfn init(config: &str) -> Result<*mut c_void, Error> {    let c_config = CString::new(config)?;    let result = unsafe { my_lib_init(c_config.as_ptr()) };    if result != 0 {        return Err(Error::InitFailed(result));    }    // return handle...    todo!()}

2. bindgen for automatic binding generation

toml
# Cargo.toml[build-dependencies]bindgen = "0.70"
rust
// build.rsuse std::path::PathBuf;
fn main() {    println!("cargo:rerun-if-changed=wrapper.h");    println!("cargo:rustc-link-lib=mylib");    println!("cargo:rustc-link-search=/usr/local/lib");
    let bindings = bindgen::Builder::default()        .header("wrapper.h")        .clang_arg("-I/usr/local/include")        .clang_arg("-DMYLIB_VERSION=2")        // Only generate bindings for this library (not system headers)        .allowlist_function("mylib_.*")        .allowlist_type("MyLib.*")        .allowlist_var("MYLIB_.*")        // Derive common traits on structs        .derive_debug(true)        .derive_default(true)        // Block problematic types        .blocklist_type("__va_list_tag")        .generate()        .expect("Unable to generate bindings");
    let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap());    bindings        .write_to_file(out_path.join("bindings.rs"))        .expect("Couldn't write bindings!");}
rust
// src/lib.rs — include generated bindings#![allow(non_upper_case_globals)]#![allow(non_camel_case_types)]#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

3. sys crate pattern

Structure:

mylib-sys/├── Cargo.toml├── build.rs        # links the library├── wrapper.h       # C headers to translate└── src/    └── lib.rs      # includes generated bindings
mylib/             # safe wrapper├── Cargo.toml└── src/    └── lib.rs
toml
# mylib-sys/Cargo.toml[package]name = "mylib-sys"version = "0.1.0"links = "mylib"          # tells Cargo this crate links libmylib
[build-dependencies]bindgen = "0.70"pkg-config = "0.3"       # for system library detection
rust
// mylib-sys/build.rsfn main() {    // Try pkg-config first    if let Ok(lib) = pkg_config::probe_library("mylib") {        for path in lib.include_paths {            println!("cargo:include={}", path.display());        }        return;    }
    // Fallback: compile from vendored source    cc::Build::new()        .file("vendor/mylib/src/mylib.c")        .include("vendor/mylib/include")        .compile("mylib");
    println!("cargo:rerun-if-changed=vendor/mylib/src/mylib.c");}

4. Writing safe wrappers

rust
// mylib/src/lib.rsuse mylib_sys as ffi;use std::ffi::{CStr, CString};
pub struct MyLib {    handle: *mut ffi::mylib_t,}
// Safety: handle is not shared across threadsunsafe impl Send for MyLib {}unsafe impl Sync for MyLib {}
impl MyLib {    pub fn new(config: &str) -> Result<Self, Error> {        let c_config = CString::new(config).map_err(|_| Error::InvalidConfig)?;        let handle = unsafe { ffi::mylib_create(c_config.as_ptr()) };        if handle.is_null() {            return Err(Error::InitFailed);        }        Ok(Self { handle })    }
    pub fn process(&mut self, data: &[u8]) -> Result<usize, Error> {        let result = unsafe {            ffi::mylib_process(self.handle, data.as_ptr(), data.len())        };        if result < 0 {            return Err(Error::ProcessFailed(result));        }        Ok(result as usize)    }}
impl Drop for MyLib {    fn drop(&mut self) {        unsafe { ffi::mylib_destroy(self.handle) };    }}

5. Exporting Rust to C with cbindgen

toml
# Cargo.toml[build-dependencies]cbindgen = "0.27"
rust
// src/lib.rs — exported Rust API#[no_mangle]pub extern "C" fn mylib_create(config: *const std::ffi::c_char) -> *mut MyLib {    // ...    Box::into_raw(Box::new(instance))}
#[no_mangle]pub extern "C" fn mylib_destroy(ptr: *mut MyLib) {    if !ptr.is_null() {        unsafe { drop(Box::from_raw(ptr)) };    }}
#[no_mangle]pub extern "C" fn mylib_process(    ptr: *mut MyLib,    data: *const u8,    len: usize,) -> std::ffi::c_int {    let lib = unsafe { &mut *ptr };    match lib.process(unsafe { std::slice::from_raw_parts(data, len) }) {        Ok(n) => n as std::ffi::c_int,        Err(_) => -1,    }}
rust
// build.rsfn main() {    let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();    cbindgen::Builder::new()        .with_crate(crate_dir)        .with_language(cbindgen::Language::C)        .generate()        .expect("Unable to generate C bindings")        .write_to_file("include/mylib.h");}

6. Linking libraries in build.rs

rust
// build.rs — common patternsfn main() {    // Static library    println!("cargo:rustc-link-lib=static=mylib");    println!("cargo:rustc-link-search=native=/path/to/lib");
    // Dynamic library    println!("cargo:rustc-link-lib=dylib=mylib");
    // Framework (macOS)    println!("cargo:rustc-link-lib=framework=CoreFoundation");
    // Build C source with cc crate    cc::Build::new()        .file("src/helper.c")        .flag("-std=c11")        .compile("helper");}

For bindgen and cbindgen configuration details, see references/bindgen-cbindgen.md [blocked].

Related skills

  • Use skills/rust/rustc-basics for RUSTFLAGS affecting FFI builds
  • Use skills/rust/cargo-workflows for build.rs integration and sys crate layout
  • Use skills/zig/zig-cinterop for Zig's equivalent C interop approach
  • Use skills/binaries/dynamic-linking for dynamic library linking details

來源與署名

來源:mohitmishra786/low-level-dev-skills位於skills/rust/rust-ffi提交bdc5847

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架