This crate makes it possible to define your own PhantomData and similarly behaved unit types with generic parameters, which is not permitted in ordinary Rust.
[dependencies]
ghost = "0.1"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"]pubstructPhantomData<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.
structMyPhantom<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]structMyPhantom<T: ?Sized>;fnmain(){// 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.traitTrait{}impl<T>Traitfor std::marker::PhantomData<T>{}impl<T>TraitforMyPhantom<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)]structCrazy<'a,V:'a,T>where&'aV:IntoIterator<Item = T>;fnmain(){let _ = Crazy::<'static,Vec<String>,&'staticString>;// 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 parameter
The implications of variance are explained in more detail by the Subtyping chapter of the Rustonomicon.
use ghost::phantom;#[phantom]structContravariantLifetime<#[contra]'a>;fnf<'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]structDemo<A,#[contra]B,#[invariant]C>;There are two alternatives for how to handle Rustdoc documentation on publicly exposed phantom types.
You may provide documentation directly on the phantom struct in the obvious way,
but Rustdoc will blithely display the somewhat distracting implementation
details of the mechanism emitted by the #[phantom] macro. This way should be
preferred if you need to document any public methods, as methods will not be
visible in the other alternative.
use ghost::phantom;/// Documentation.#[phantom]pubstructMyPhantom<T: ?Sized>;impl<T: ?Sized>MyPhantom<T>{/// Documentation on methods.pubfnfoo(){}}If you aren't adding methods or don't need methods to be rendered in the documentation, the recommended idiom is as follows. Rustdoc will show a much less distracting type signature and all of your trait impls, but will not show inherent methods.
mod private {use ghost::phantom;#[phantom]pubstructMyPhantom<T: ?Sized>;}/// Documentation goes here.#[allow(type_alias_bounds)]pubtypeMyPhantom<T: ?Sized> = private::MyPhantom<T>;#[doc(hidden)]pubuseself::private::*;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 inRegistry::<Flag>{/* ... */}Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.