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 Rustonomicon states:
To do this in Rust, let’s create our own opaque types:
#[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)>,
}
By including at least one private field and no constructor,
we create an opaque type that we can't instantiate outside of this module.
(A struct with no field could be instantiated by anyone.)
We also want to use this type in FFI, so we have to add #[repr(C)]
.
The marker ensures the compiler does not mark the struct as Send
, Sync
and Unpin
are
not applied to the struct. (*mut u8
is not Send
or Sync
, PhantomPinned
is not Unpin
)
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 {
_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();
}
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.