mcpField
R
rust-ffi

rust-ffi

github.com/mohitmishra786開発269573 回インストール18 週間前に更新
リポジトリ

rust-ffi について

rust-ffiは、RustとCライブラリの連携や、Rust APIのC向け公開を扱うエージェント向けスキルです。バインディング生成、安全なラッパー、ライブラリのリンク、sysクレートの構成を解説します。

主な機能

  1. 01C関数の手動宣言とbindgen
  2. 02cbindgenによるRust APIの公開
  3. 03ネイティブライブラリ用sysクレート
  4. 04unsafe APIの安全なラッパー
  5. 05build.rsでのライブラリリンク設定

活用シーン

  1. 01RustからCライブラリを呼び出す
  2. 02CヘッダーからRustバインディングを生成
  3. 03CコードにRust関数を公開
  4. 04Cargoプロジェクトでネイティブライブラリをリンク

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill rust-ffi

インストール後、Claude Code で/rust-ffiを入力して読み込まれたか確認します。

同梱ファイルreferences/bindgen-cbindgen.md

詳細

Rust FFI

目的

Rust の Foreign Function Interface(FFI)についてエージェントをガイドします。bindgen を使った Rust から C の呼び出し、cbindgen を使った C から呼び出すための Rust のエクスポート、安全なラッパーの作成、build.rs によるライブラリのリンク、sys クレートの構成を扱います。

トリガー

  • 「Rust から C ライブラリを呼び出すにはどうすればよいですか?」
  • 「bindgen を使って Rust バインディングを生成するにはどうすればよいですか?」
  • 「C から呼び出せるように Rust 関数をエクスポートするにはどうすればよいですか?」
  • 「unsafe な C API を安全なラッパーで包むにはどうすればよいですか?」
  • 「Rust でシステムライブラリをリンクするにはどうすればよいですか?」
  • 「sys クレートとは何ですか? どのように構成すればよいですか?」

ワークフロー

1. bindgen を使わずに C を呼び出す(手動で宣言)
// Declare external C functions manually
use 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 safely
fn 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 によるバインディングの自動生成
# Cargo.toml
[build-dependencies]
bindgen = "0.70"
// build.rs
use 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!");
}
// 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 クレートのパターン

構成:

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
# 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
// mylib-sys/build.rs
fn 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. 安全なラッパーの作成
// mylib/src/lib.rs
use mylib_sys as ffi;
use std::ffi::{CStr, CString};

pub struct MyLib {
    handle: *mut ffi::mylib_t,
}

// Safety: handle is not shared across threads
unsafe 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. cbindgen を使って C 向けに Rust をエクスポートする
# Cargo.toml
[build-dependencies]
cbindgen = "0.27"
// 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,
    }
}
// build.rs
fn 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. build.rs でライブラリをリンクする
// build.rs — common patterns
fn 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");
}

bindgen と cbindgen の設定の詳細については、references/bindgen-cbindgen.md を参照してください。

関連スキル

  • FFI ビルドに影響する RUSTFLAGS については、skills/rust/rustc-basics を使用します
  • build.rs の統合と sys クレートの構成については、skills/rust/cargo-workflows を使用します
  • Zig による同等の C 相互運用については、skills/zig/zig-cinterop を使用します
  • 動的ライブラリのリンクの詳細については、skills/binaries/dynamic-linking を使用します

コメント