给定一个结构:
#[repr(C)]
pub struct User {
pub name: *const c_char,
pub age: u8,
pub ctx: ??,
}
字段ctx
只能被C代码操纵;它是指向C struct UserAttr
的指针。
根据the Rust FFI documentation,选择将被定义为不透明类型pub enum UserAttr {}
。但是,我发现Rust无法复制其值,例如why does the address of an object change across methods。
在Rust中定义这样一个不透明指针的正确方法是什么,以便它的值(作为指针)被复制到方法中?
RFC 1861介绍了外部类型的概念。虽然已经实施,但尚未稳定下来。一旦它,它将成为首选的实现:
#![feature(extern_types)]
extern "C" {
type Foo;
}
type FooPtr = *mut Foo;
要在Rust中执行此操作,让我们创建自己的opaque类型:
#[repr(C)] pub struct Foo { private: [u8; 0] } #[repr(C)] pub struct Bar { private: [u8; 0] } extern "C" { pub fn foo(arg: *mut Foo); pub fn bar(arg: *mut Bar); }
通过包含私有字段而不包含构造函数,我们创建了一个opaque类型,我们无法在此模块之外实例化。空数组既是零大小又与
#[repr(C)]
兼容。但是因为我们的Foo
和Bar
类型不同,我们将在它们两者之间获得类型安全,所以我们不会意外地将指针传递给Foo
到bar()
。
创建一个不透明的指针,这样就没有正常的方法来创建这样的类型;你只能创建它的指针。
mod ffi {
use std::ptr;
pub struct MyTypeFromC { _private: [u8; 0] }
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
因此它是一个不透明的指针。移动struct MyRustType
不会改变指针的值。
此答案的先前迭代和文档建议使用空枚举(enum MyTypeFromC {}
)。没有变体的枚举在语义上等同于从不类型(!
),这是一种不存在的类型。有人担心使用这样的构造可能会导致未定义的行为,因此转移到空阵列被认为更安全。