[−][src]Crate phantasm
This crate is aiming to make work with variance easier.
The crate exposes 3 types - Invariant<T>
, Covariant<T>
and
Contravariant<T>
with corresponding variance over T
those work
very similar to PhantomData<_>
.
motivation
In rust it's an error to have an unused generic param in struct:
struct Slice<'a, T> { start: *const T, end: *const T, }
error[E0392]: parameter `'a` is never used
--> src/lib.rs:16:14
|
3 | struct Slice<'a, T> {
| ^^ unused parameter
|
= help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData`
This is an error because rust compiler doesn't know should Slice
be
covariant, contravariant or invariant over 'a
. (What it means is that
rustc doesn't know if Slice<'static, _>
should be Slice<'a, _>
or vice
versa or none of this. See Subtyping and Variance nomicon chapter for
better explanation)
To mitigate this issue and control the variance there is a type called
core::marker::PhantomData<T>
. PhantomData<T>
is a zst type
that acts like it owns T
.
However PhantomData
come with a number of issues
- Variance is a hard thing itself, but
PhantomData
makes it even harder to understand. It's not so clear to understand what does smt likePhantomData<fn(A, B) -> B>
(contravariant overA
and invariant overB
) - Sometimes it works badly in const context (see next paragraph)
phantasm
's naming somehow helps woth 1.
making code clearer (though
variance still is a hard-to-understand thing)
function pointers in const fn are unstable
It's common to make a type invariant over T
with PhantomData<fn(T) -> T>
however, if you've ever tryed to use it in const fn
, you know that it's
painfull (see rust-lang/69459 and rust-lang/67649).
This crate helps mitigate this issue:
use phantasm::Invariant; pub struct Test<T>(Invariant<T>); impl<T> Test<T> { pub const fn new() -> Self { Self(Invariant) // just works } }
life
For variance over lifetimes, use &'l ()
:
use phantasm::{Contravariant, Covariant, Invariant}; struct Test<'a, 'b, 'c>(Invariant<&'a ()>, Covariant<&'b ()>, Contravariant<&'c ()>);
comparison operators cannot be chained
Note: you can't use Invariant<Ty>
as a value (just as
PhantomData
). To create Invariant<Ty>
value use Invariant::<Ty>
(same goes for both Covariant<T>
and Contravariant<T>
)
// won't compile let _ = phantasm::Invariant<i32>;
use phantasm::Invariant; // ok let _ = Invariant::<i32>; // Both forms are acceptable in type possition struct A<T>(Invariant<T>); struct B<T>(Invariant<T>);
many types
When you need to set variance of many types at once, just use typle:
struct Test<A, B>(phantasm::Covariant<(A, B)>);
Type Definitions
Contravariant | Marker zst type that is contravariant over |
Covariant | Marker zst type that is covariant over |
Invariant | Marker zst type that is invariant over |