Define your own PhantomData and similarly behaved unit types.
PhantomData
as defined by the Rust standard library is magical in that
the same type is impossible to define in ordinary Rust code. It is defined
in the standard library like this:
#[lang = "phantom_data"]
pub struct PhantomData<T: ?Sized>;
The #[lang = "..."]
attribute indicates that this is a lang item, a
special case known to the compiler. It is the only type permitted to carry
an unused type parameter.
If we try to define an equivalent unit struct with type parameter, the compiler rejects that.
struct MyPhantom<T: ?Sized>;
error[E0392]: parameter `T` is never used
--> src/main.rs:1:18
|
1 | struct MyPhantom<T: ?Sized>;
| ^ unused type parameter
|
= help: consider removing `T` or using a marker such as `std::marker::PhantomData`
This crate provides a #[phantom]
attribute that makes it possible to
define unit structs with generic parameters.
use ghost::phantom;
#[phantom]
struct MyPhantom<T: ?Sized>;
fn main() {
// Proof that MyPhantom behaves like PhantomData.
let _: MyPhantom<u8> = MyPhantom::<u8>;
assert_eq!(0, std::mem::size_of::<MyPhantom<u8>>());
}
// Proof that MyPhantom is not just a re-export of PhantomData.
// If it were a re-export, these would be conflicting impls.
trait Trait {}
impl<T> Trait for std::marker::PhantomData<T> {}
impl<T> Trait for MyPhantom<T> {}
// Proof that MyPhantom is local to the current crate.
impl<T> MyPhantom<T> {
}
The implementation accepts where-clauses, lifetimes, multiple generic parameters, and derives. Here is a contrived invocation that demonstrates everything at once:
use ghost::phantom;
#[phantom]
#[derive(Copy, Clone, Default, Hash, PartialOrd, Ord, PartialEq, Eq, Debug)]
struct Crazy<'a, V: 'a, T> where &'a V: IntoIterator<Item = T>;
fn main() {
let _ = Crazy::<'static, Vec<String>, &'static String>;
// Lifetime elision.
let crazy = Crazy::<Vec<String>, &String>;
println!("{:?}", crazy);
}
The #[phantom]
attribute accepts attributes on individual generic
parameters (both lifetime and type parameters) to make them contravariant or
invariant. The default is covariance.
#[contra]
— contravariant generic parameter#[invariant]
— invariant generic parameterThe implications of variance are explained in more detail by the Subtyping chapter of the Rustonomicon.
use ghost::phantom;
#[phantom]
struct ContravariantLifetime<#[contra] 'a>;
fn f<'a>(arg: ContravariantLifetime<'a>) -> ContravariantLifetime<'static> {
// This coercion is only legal because the lifetime parameter is
// contravariant. If it were covariant (the default) or invariant,
// this would not compile.
arg
}
#[phantom]
struct Demo<A, #[contra] B, #[invariant] C>;
Entirely up to your imagination. Just to name one, how about a typed registry library that admits the following syntax for iterating over values registered of a particular type:
for flag in Registry::<Flag> {
/* ... */
}