← ClaudeAtlas

rust-ffilisted

Design an FFI boundary meant to last — a thin translation layer with the logic in core crates, no panic across the boundary, explicit ownership on every pointer, repr(transparent) newtypes, and unsafe extern in edition 2024. Use when designing or reviewing a Rust FFI surface, when exposing a Rust library to another language, when a DLL or shared library keeps state, or when the user asks how to shape an extern function.
rewrite-rs/skills · ★ 1 · AI & Automation · score 74
Install: claude install-skill rewrite-rs/skills
# Rust FFI A boundary that is meant to stay is designed, not discovered. Moving off C — bindgen, cbindgen, and the link into an existing build — is `/port-from-c`. ## The layer only translates Every line of business logic lives in a normal Rust crate that knows nothing about FFI; the `extern` layer converts types, checks pointers, and calls in. The core crate is testable with `cargo test`, the FFI layer only through a foreign harness — logic that drifts into the boundary is logic no test reaches. ```rust use std::ffi::{c_char, CStr}; use std::panic::catch_unwind; // Core: the logic, in code that knows nothing about FFI. pub struct Client { pub name: String, } impl Client { fn new(name: &str) -> Self { Client { name: name.to_owned() } } } // Boundary: translates, checks, and calls in — nothing else. #[repr(transparent)] pub struct MylibClient(*mut Client); /// # Safety /// `name` must be NUL-terminated UTF-8, or null; the check happens once, /// here, on the way in. #[unsafe(no_mangle)] pub unsafe extern "C" fn mylib_client_new(name: *const c_char) -> *mut MylibClient { let build = || { if name.is_null() { return None; } let name = unsafe { CStr::from_ptr(name) }.to_str().ok()?; Some(Box::into_raw(Box::new(Client::new(name))) as *mut MylibClient) }; catch_unwind(build).ok().flatten().unwrap_or(std::ptr::null_mut()) } /// # Safety /// `client` must be a non-null handle from `mylib_client_new`, freed exactly once.