I originally posted this question on Stack Overflow. Someone elected to open an Github issue for rust-lang on the topic, and a commenter suggested that posing the question here would could get more traction.
I'm looking at rewriting part of a Rust library which wraps a C library because the current wrapper is unsafe. The C library expects a function pointer of the form
void(*)(...), so the obvious choice on the Rust side would be a function pointer of type
extern "C" fn(...) -> (). For safety reasons, I would like to create a type family
Invariant<'a>, which is invariant with respect to
'a, such that we can safely transmute a function pointer of type
extern "C" fn(...) -> Invariant<'a> to a function pointer of type
extern "C" fn(...) -> (). Users of my wrapped library would then pass a function pointer that returns an
In order to make such a transmutation safe, we need
Invariant<'a> to have the same size, alignment, and ABI as
(). Surprisingly, there seems to be no way to do this that's guaranteed to work. I've tried quite a few tricks with
#[repr(transparent)], but nothing seems to work (at least according to Miri) (see the Stack Overflow question for some of my attempts, one of which passes
#[deny(improper_ctypes_definitions)], but all of which fail Miri). The issue is that
#[repr(transparent)] is only guaranteed to work for structs which have a single non-zero-sized field (and for equivalent enums). In fact, according to the Rust Reference and the Rustonomicon, you're not actually allowed to use
#[repr(transparent)] on a struct where all fields are zero-sized (although the compiler will usually allow this anyway). There was a
pull request to try to address this, but this pull request seems to be in limbo.
Is there some technique I haven't thought of for creating a newtype wrapper around
() that lets us do what I want? In general, is there a way to create a
newtype wrapper around a ZST?
If not, how can we modify
#[repr(transparent)] to allow this? My proposal would be that if you have a
#[repr(transparent)] struct where all fields are ZSTs, you should be able to designate a single field which will be the
newtype-wrapped field. However, I'm not exactly sure how this will work with generics - there may be some technical issues I haven't thought of.