What's the intended behavior of c_void?


I’m wondering what the deal with libc::c_void is. It’s currently defined as an enum with two private variants and #[repr(u8)], as of #11539. I’m not sure how that’s better than struct c_void(u8); if the intention is just to make sure *const c_void is opaque and represented as i8* in llvm.

Back in the day [1907], apparently the intention was indeed that empty enums were to be uncopyable, while more recently [10278] it was decided that they can be copyable after all. There’s also a test that lives at src/test/compile-fail/non-copyable-void.rs that sort of incidentally fails to document that evolution of thinking, as it currently ensures that c_void doesn’t accidentally implement Clone [0dfc9], while it used to check that its not explicitly copyable [e6ab0], and originally that it’s not copyable at all [ab410].

Meanwhile, of course, c_void is neither an empty enum nor is it not Copy and indeed it’s entirely possible to dereference pointers to empty enums and enums with private variants in unsafe code and happily copy the resulting values around. With ptr::read, even affine types can readily be copied, so trying to stop people who already somehow got hold of raw pointers from dereferencing them is sort of a losing battle, but maybe a more opaque yet i8-looking definition might be enum c_void {}; impl Drop for c_void { ... }; [17987] …