给定一个结构:
#[repr(C)]
pub struct User {
pub name: *const c_char,
pub age: u8,
pub ctx: ??,
}
Run Code Online (Sandbox Code Playgroud)
该字段ctx只能由C代码操作;它是C结构的指针UserAttr。
根据Rust FFI文档,该选择将定义为不透明类型pub enum UserAttr {}。但是,我发现Rust无法复制其值,例如,为什么对象的地址在方法之间会发生变化。
在Rust中定义这样一个不透明指针的正确方法是什么,以使它的值(作为指针)跨方法复制?
RFC 1861引入了外部类型的概念。虽然已实施,但尚未稳定。一旦完成,它将成为首选实现:
#![feature(extern_types)]
extern "C" {
type Foo;
}
type FooPtr = *mut Foo;
Run Code Online (Sandbox Code Playgroud)
该文档指出:
为了在Rust中做到这一点,让我们创建自己的不透明类型:
Run Code Online (Sandbox Code Playgroud)#[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); }通过包含一个私有字段而不包含构造函数,我们创建了一个不透明的类型,该类型无法在该模块之外实例化。空数组的大小为零,并且与兼容
#[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();
}
Run Code Online (Sandbox Code Playgroud)
分解一下:
#![feature(extern_types)]
extern "C" {
type Foo;
}
type FooPtr = *mut Foo;
Run Code Online (Sandbox Code Playgroud)
因此,它是一个不透明的指针。移动该结构MyRustType不会更改指针的值。
该答案的先前迭代和建议的文档都使用一个空的枚举(enum MyTypeFromC {})。没有变体的枚举在语义上等效于Never类型(!),它是不存在的类型。有人担心使用这种构造可能会导致未定义的行为,因此将其移到空数组被认为更安全。