直接回答:FFI 边界上 Rust 的编译器保护全部失效,安全全靠人工纪律。核心规则:指针进入 Rust 前校验非空与对齐,用 slice::from_raw_parts 重建 slice 时长度必须真实;所有权穿越边界要用 Box::into_raw/Box::from_raw 配对,谁分配谁释放,绝不能 Rust 分配 C 释放(分配器不同即 UB);字符串只传 CString,C 侧返回的 char* 不要直接 from_raw 夺所有权除非契约允许。

展开解析:ABI 与类型细节:结构体要 #[repr(C)] 保证内存布局;extern "C" 函数默认不 unwind,Rust panic 穿过 FFI 边界是 UB,需要 catch_unwind 兜住;回调场景把 Rust 闭包装箱成 *mut c_void 透传,在 trampoline 里还原。生命周期陷阱最阴险:C 侧持有 Rust 传入的指针,而 Rust 侧数据已 drop,产生悬垂——对策是明确契约:要么 C 只借用并在调用返回后失效,要么所有权移交并注册析构回调。工程配套:用 cbindgen/bindgen 自动生成头文件与绑定避免手写漂移;用 Miri 加 ASan 跑集成测试;把 FFI 包在最小 unsafe 模块内,对外只暴露 safe API。

追问方向:为什么说 panic 跨过 FFI 是 UB?#[repr(C)] 保证了什么不保证什么?如何处理 C 侧的错误码映射?

(约 450 字)