什么是Rust惯用法来定义指向C透明指针的字段?

wan*_*gii 1 rust

给定一个结构:

#[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中定义这样一个不透明指针的正确方法是什么,以使它的值(作为指针)跨方法复制?

She*_*ter 6

未来

RFC 1861引入了外部类型的概念。虽然已实施,但尚未稳定。一旦完成,它将成为首选实现:

#![feature(extern_types)]

extern "C" {
    type Foo;
}

type FooPtr = *mut Foo;
Run Code Online (Sandbox Code Playgroud)

今天

该文档指出:

为了在Rust中做到这一点,让我们创建自己的不透明类型:

#[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);
}
Run Code Online (Sandbox Code Playgroud)

通过包含一个私有字段而不包含构造函数,我们创建了一个不透明的类型,该类型无法在该模块之外实例化。空数组的大小为零,并且与兼容#[repr(C)]。但是,由于我们的 FooBar类型不同,因此我们将在两者之间获得类型安全性,因此我们不能无意间将指针传递Foobar()

创建一个不透明的指针,使得没有正常的方法可以创建这种类型。您只能创建指向它的指针。

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类型(!),它是不存在的类型。有人担心使用这种构造可能会导致未定义的行为,因此将其移到空数组被认为更安全。