给定一个结构:
#[repr(C)]
pub struct User {
pub name: *const c_char,
pub age: u8,
pub ctx: ??,
}
字段ctx
只能由 C 代码操作;它是指向 C 结构UserAttr
的指针。
根据 Rust FFI 文档,该选择将被定义为不透明类型 pub enum UserAttr {}
。但是,我发现 Rust 无法复制其值,例如为什么对象的地址会跨方法更改。
在 Rust 中定义这样一个不透明的指针的正确方法是什么,以便它的值(作为指针)跨方法复制?
未来
RFC 1861 引入了外部类型的概念。虽然已实施,但尚未稳定下来。一旦成为,它将成为首选实现:
#![feature(extern_types)]
extern "C" {
type Foo;
}
type FooPtr = *mut Foo;
今天
Rustonomicon指出:
要在 Rust 中做到这一点,让我们创建我们自己的不透明类型:
#[repr(C)] pub struct Foo { _data: [u8; 0], _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, } #[repr(C)] pub struct Bar { _data: [u8; 0], _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, }
通过包含至少一个私有字段和不构造函数,我们创建了一个不透明类型,我们无法在此模块之外实例化。(没有字段的结构可以由任何人实例化。我们也想在 FFI 中使用这种类型,所以我们必须添加
#[repr(C)]
.标记确保编译器不会将结构标记为Send
,Sync
和Unpin
不应用于结构。(*mut u8
不是Send
或Sync
,PhantomPinned
不是Unpin
)
创建不透明指针时,没有创建此类类型的正常方法;只能创建指向它的指针。
mod ffi {
use std::ptr;
pub struct MyTypeFromC {
_data: [u8; 0],
_marker:
core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
pub fn constructor() -> *mut MyTypeFromC {
ptr::null_mut()
}
pub fn something(_thing: *mut MyTypeFromC) {
println!("Doing a thing");
}
}
use ffi::*;
struct MyRustType {
score: u8,
the_c_thing: *mut MyTypeFromC,
}
impl MyRustType {
fn new() -> MyRustType {
MyRustType {
score: 42,
the_c_thing: constructor(),
}
}
fn something(&mut self) {
println!("My score is {}", self.score);
ffi::something(self.the_c_thing);
self.score += 1;
}
}
fn main() {
let mut my_thing = MyRustType::new();
my_thing.something();
}
分解一下:
// opaque -----V~~~~~~~~~V
*mut MyTypeFromC
// ^~~^ ------------ pointer
因此,它是一个不透明的指针。移动结构MyRustType
不会更改指针的值。
往事
此答案和文档的先前迭代建议使用空枚举 ( enum MyTypeFromC {}
)。没有变体的枚举在语义上等价于永不类型(!
),后者是不可能存在的类型。有人担心使用这样的结构可能会导致未定义的行为,因此移动到空数组被认为更安全。