Library for writing const-compatible phantom types with explicit variance and autotrait behavior #21
Description
When writing data structures using unsafe code or otherwise unused type parameters, library authors often reach for PhantomData<T> without adequate consideration.
pub struct IterEmpty<T>(PhantomData<T>);
impl<T> Iterator for IterEmpty<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
You might have missed it (as the standard library did too) that PhantomData<T> is not the right phantom type here. For the empty iterator the phantom type should be Send and Sync regardless of whether T is Send and Sync, as no T ever exists.
The canonical covariant-always-Send-and-Sync type in the standard library is fn() -> T
so a library author who realizes PhantomData<T> is not right would write:
pub struct IterEmpty<T>(PhantomData<fn() -> T>);
This is still a bad choice because function pointers can't exist under the conservative const fn feature gate.
impl<T> IterEmpty<T> {
pub const fn new() -> Self {
IterEmpty(PhantomData)
}
}
error: function pointers in const fn are unstable
--> src/libcore/iter/sources.rs:277:15
|
277 | IterEmpty(PhantomData)
| ^^^^^^^^^^^
There are hacks; the next step would be
pub struct IterEmpty<T>(PhantomData<PhantomFnWorkaround<T>>);
struct PhantomFnWorkaround<T>(fn() -> T);
and this is ideal in all ways except that it is:
- nasty to read,
- nasty to write,
- nasty to remember,
- nasty to review.
I want a library for emitting correct const-compatible phantom types (like the last correct solution above) that puts variance and autotrait behavior front and center for readers and reviewers.
pub struct IterEmpty<T>(phantom_data![T, Covariant + AlwaysSendSync]);
It should support any combination of {Covariant, Contravariant, Invariant} × {AlwaysSendSync, NeverSendSync, InheritSendSync}.
Syntax alternative:
#[phantom(Covariant, AlwaysSendSync)]
pub struct IterEmpty<T>(PhantomData);
// expands to
pub struct IterEmpty<T>(PhantomData<$the_crate::CovariantAlwaysSendSync<T>>);