Rust中零大小返回类型的FFI安全规则及合规实现问询
需求:定义FFI安全的零大小不变生命周期类型族
需要实现一个零大小类型族Invariant<'a>,满足以下要求:
- 类型不变地依赖生命周期
'a(通过PhantomData<Cell<&'a _>>实现不变性) - 严格FFI安全:可以安全地将返回
Invariant<'a>的extern "C"函数指针转换为返回()的extern "C"函数指针(对应C端的void返回函数,无需C端存在该零大小类型) - 禁止公开构造
Invariant<'a>,仅通过类型的存在证明已执行特定操作 - 定义返回该类型的
extern "C"函数时,必须通过#[deny(improper_ctypes_definitions)]lint(基于rustc 1.71.1版本)
尝试1
#[repr(transparent)] pub struct Try1<'a>(PhantomData<Cell<&'a i32>>);
- 未通过
improper_ctypes_definitionslint,编译器提示:类型仅由PhantomData组成
尝试2
#[repr(transparent)] pub struct Try2<'a>((), PhantomData<Cell<&'a i32>>);
- 未通过lint,错误信息:此结构体仅包含零大小字段
- 原本期望
#[repr(transparent)]让结构体表现等同于(),但查阅文档发现,当结构体所有字段均为零大小时,#[repr(transparent)]的行为并未明确规定
尝试3
#[repr(transparent)] pub struct GenericInvariant<'a, T>(T, PhantomData<Cell<&'a i32>>); pub type Try3<'a> = GenericInvariant<'a, ()>;
- 能够通过
improper_ctypes_definitionslint,但会引入额外的公开泛型类型GenericInvariant<'a, T>,造成命名空间污染(实际仅需Try3<'a>)
尝试4
#[repr(transparent)] struct GenericInvariant<'a, T>(T, PhantomData<Cell<&'a i32>>); #[repr(transparent)] pub struct Try4<'a>(GenericInvariant<'a, ()>);
- 未通过lint,错误信息:此结构体仅包含零大小字段,与对
#[repr(transparent)]的直觉预期不符
补充测试验证
编写以下测试代码并使用Miri运行:
extern "C" fn f(&()) -> Invariant { todo!() } let g: extern "C" fn(&()) -> () = unsafe { std::mem::transmute(f as extern "C" fn(&()) -> Invariant) }; g(&())
- Miri判定所有上述尝试的实现均会导致未定义行为
内容的提问来源于stack exchange,提问作者Mark Saving
相关产品推荐
相关产品推荐

