Skip to content
Development
Skill

/rust-ffi

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,

From plugin
low-level-dev-skills
159142 skills
Install
$ npx -y skills add mohitmishra786/low-level-dev-skills --skill rust-ffi --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/rust-ffi

Context preview

The summary Claude sees to decide when to auto-load this skill.

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,

SKILL.md

rust-ffi.SKILL.md
name: rust-ffi
description: 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.

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)

// 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 for automatic binding generation

# 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 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
# 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. Writing safe wrappers

// 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. Exporting Rust to C with cbindgen

# 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_cra
Read more
Ships withlow-level-dev-skills

A curated suite of AI agent skills for systems and low-level programming — C/C++, Rust, Zig, GPU, bare-metal firmware, Linux kernel/driver development, computer architecture, compiler internals, HPC, and more.

Get the whole plugin
Stats
172
Stars
24
Forks
Maintained
Maintenance
JavaScript
Language
MIT
License
1mo ago
Last commit
5mo ago
Created

Repo: mohitmishra786/low-level-dev-skills

Other skills on low-level-dev-skills.