Currently, rust does not like it when you pass Zero Sized Types (ZSTs) to extern "C"
functions, even behind a raw pointer reference like *const ZeroSized
. For example, the following code:
struct ZeroSized;
extern "C" {
fn send(opaque_ref: *const ZeroSized);
fn recv() -> *const ZeroSized;
}
Produces these warnings on today’s nightly:
warning: found zero-size struct in foreign module, consider adding a member to this struct, #[warn(improper_ctypes)] on by default
--> <anon>:7:25
|
7 | fn send(opaque_ref: *const ZeroSized);
| ^^^^^^^^^^^^^^^^
warning: found zero-size struct in foreign module, consider adding a member to this struct, #[warn(improper_ctypes)] on by default
--> <anon>:8:18
|
8 | fn recv() -> *const ZeroSized;
| ^^^^^^^^^^^^^^^^
It is true that zero sized structs cannot exist in C/C++ code, and thus it makes sense to prevent passing them by value. The primary use case for passing these ZSTs behind a raw pointer would be as an opaque data type. The current type of choice for that situation is
enum Impossible {}
This type has a serious disadvantage compared to a normal ZST, which is that if it is ever exposed to safe code as a &Impossible
, that safe code can match against it and trigger Undefined Behavior. This means that these opaque C++ objects, if they want to be passed around within rust by reference, have to be wrapped in an inconvenient and tedious to write wrapper type:
#[derive(Copy, Clone)]
struct ImpossibleRef<'a> {
ptr: *const Impossible,
_marker: PhantomData<&'a Impossible>,
}
// etc...
In contrast, it would be much nicer if they could look like normal rust types, except that their exact backing layout is unknown. One way to represent that would be to instead use a zero sized type with a private ZST member. In rust this has the advantage of:
- not triggering Undefined Behavior when passed behind a reference to safe rust code
- making it impossible for safe rust functions like
mem::swap()
to ruin the data layout of the backing C++ type
But using these types in this way causes the improper_ctypes
lint to complain.
I’d like to suggest relaxing the lint to allow for this use case, and make it possible to create nicer, safer, and more rust-like abstractions around opaque C/C++ style types, in less code.
(Anecdotally, while writing code which performs interop between C++ code in gecko, and rust code, I have often wanted to be able to use ZSTs like this)