关于 rust-ffi-safe-wrappers
面向 Rust 开发者的外部接口安全封装技能,将原始绑定限制在 unsafe 边界,并指导 Rust API 明确所有权、错误、清理和 panic 行为。
主要功能
- 01分离原始绑定与安全封装
- 02核对 ABI 和所有权约定
- 03定义兼容 C 的数据布局
- 04管理 C 字符串与句柄
- 05将外部错误转换为 Result
- 06明确 panic 与回调行为
适用场景
- 01审查 Rust 对 C 库的绑定
- 02为外部句柄封装清理逻辑
- 03检查字符串和缓冲区所有权
- 04审计 Rust 2024 FFI 声明
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ lvtd-llc/ skills --skill rust-ffi-safe-wrappers安装后在 Claude Code 中输入/rust-ffi-safe-wrappers确认已加载。
附带文件agents/openai.yamlreferences/ffi-boundary-patterns.md
详细介绍
Rust FFI 安全封装
使用此技能将外部接口封装为 Rust API,明确所有权、生命周期、错误和清理方式。FFI 代码在原始边界处应为不安全代码,对普通 Rust 调用方则应安全。
核心工作流
- 将原始声明与安全封装分开。将原始
unsafe extern绑定放在一个小型私有模块中。 - 根据外部头文件或文档核实 ABI、类型布局、调用约定、所有权、可空性、线程行为和错误约定。
- 对现代 Rust 使用
unsafe extern "C"块,尤其是在 Rust 2024 版代码中。对安全性取决于条件的项,不添加限定或显式标记为unsafe;仅当对所有 Rust 输入而言调用都安全时,才将单个 extern 项标记为safe。 - 对跨越 ABI 边界的结构体和枚举使用
#[repr(C)]。不要通过 C 暴露仅适用于 Rust 的布局类型。 - 使用
CString表示发送给 C 的、由 Rust 拥有的以 nul 结尾的字符串,使用CStr表示借用的 C 字符串。绝不要将任意 C 内存视为由 Rust 拥有。 - 将原始句柄封装为字段私有的 Rust 类型,并通过
Drop使用匹配的外部释放函数进行清理。 - 防止 Rust panic 穿过
extern "C"边界。捕获 panic,或仅在有意使用"C-unwind"时暴露允许展开的 ABI。
封装规则
添加或审查 FFI 绑定前,请阅读 references/ffi-boundary-patterns.md。
- 优先使用
core::ffi或std::ffi中的 C 类型,不要猜测整数大小。 - 在封装边界将外部错误码转换为 Rust
Result。 - 如有用,可在内部将可空句柄表示为
Option<NonNull<T>>,但向调用方暴露安全的 Rust 类型。 - 当编译器无法识别借用值与所有者之间的关系时,通过常规引用或
PhantomData将借用值绑定到所有者的生命周期。 - 如果外部库要求进程级初始化,请使用
OnceLock、LazyLock或Once,确保初始化和关闭操作可安全重复执行。 - 对大型或频繁变动的 C 头文件使用
bindgen,将 Rust API 导出为 C 时使用cbindgen,但仍须审查生成的不安全签名。
Rust 2024 语法检查
unsafe extern "C" {
pub fn library_open(path: *const core::ffi::c_char) -> *mut RawHandle;
pub fn library_close(handle: *mut RawHandle);
// Mark an item `safe` only when calling it is valid for all Rust inputs:
// pub safe fn library_version() -> core::ffi::c_int;
}
// SAFETY: This exported symbol name is unique for this library.
#[unsafe(no_mangle)]
pub extern "C" fn plugin_version() -> core::ffi::c_int {
1
}
在 Rust 2024 版代码中,no_mangle、export_name 和 link_section 等不安全属性需要使用 unsafe(...) 形式。如果符号唯一性或链接器行为属于契约的一部分,请添加 SAFETY 注释。
审查清单
- 原始绑定为私有,或与安全封装明确分开。
- 每个指针参数都注明了可空性和所有权规则。
- 每次内存分配都由同一方释放,或由明确匹配的释放函数释放。
- 字符串和缓冲区保留了长度、编码和 nul 字节要求。
- 明确规定了跨边界的 panic、回调和线程行为。
评论
支持 Markdown