What's the Rust idiom to define a field pointing to a C opaque pointer?

前端 未结 1 1484
醉话见心
醉话见心 2020-11-27 22:59

Given a struct:

#[repr(C)]
pub struct User {
    pub name: *const c_char,
    pub age: u8,
    pub ctx: ??,
}

the field ctx wo

相关标签:
1条回答
  • 2020-11-27 23:29

    The future

    RFC 1861 introduced the concept of an extern type. While implemented, it is not yet stabilized. Once it is, it will become the preferred implementation:

    #![feature(extern_types)]
    
    extern "C" {
        type Foo;
    }
    
    type FooPtr = *mut Foo;
    

    Today

    The documentation states:

    To do this in Rust, let’s create our own opaque types:

    #[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);
    }
    

    By including a private field and no constructor, we create an opaque type that we can’t instantiate outside of this module. An empty array is both zero-size and compatible with #[repr(C)]. But because our Foo and Bar types are different, we’ll get type safety between the two of them, so we cannot accidentally pass a pointer to Foo to bar().

    An opaque pointer is created such that there's no normal way of creating such a type; you can only create pointers to it.

    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();
    }
    

    Breaking it down a bit:

    // opaque -----V~~~~~~~~~V
              *mut MyTypeFromC
    //        ^~~^ ------------ pointer
    

    Thus it's an opaque pointer. Moving the struct MyRustType will not change the value of the pointer.

    The past

    Previous iterations of this answer and the documentation suggested using an empty enum (enum MyTypeFromC {}). An enum with no variants is semantically equivalent to the never type (!), which is a type that cannot exist. There were concerns that using such a construct could lead to undefined behavior, so moving to an empty array was deemed safer.

    0 讨论(0)
提交回复
热议问题