Actually, I take this idea back. If we’re going to give _: T in structs a magic meaning, then it should behave exactly like _marker: PhantomData<T> behaves today, except as before
-
_ cannot be initialized in struct expressions or extracted in struct patterns, or accessed with a dot.
-
_ can be repeated.
But really it feels silly to leave out tuple structs at this point, so maybe we should just introduce the syntax struct K { phantom T, u: U } (note: no colon), where phantom T is the same as _: T above, except struct K(phantom T); is now reasonable, too. (Syntactically it’s something like a visibility?)
PhantomData is no longer a lang item and is instead defined as struct PhantomData<T>(phantom T);
Bonus extra-silly footgunny version: introduce the phantom visibility, introduce _ as a valid field name (with the semantics of not-accessible-and-no-drop-glue, behaving like a inaccessible [u8; size_of::<T>()]; you could now instead define PhantomData as
struct Phantom<T> {
phantom _: T,
}
As a bonus, you get a slightly cleaner way to play stupid games with transmute
// Must fit in %eax for the purposes of sysexit
#[repr(packed)]
struct SyscallErrorPadded {
inner_err: Option<SyscallError>,
_: [u8; size_of::<u32>() - size_of::<SyscallError>()],
}
// ...
asm!("sysexit" :: "{eax}"(transmute::<_, u32>(SyscallErrorPadded{ inner_err: err }) ));