Expand description
Marker types to indicate precisely the variance relationship between a generic type and its parameters.
Rust supports three different modes of variance between a generic type F
and a type parameter T
:1
- Covariance:
F<T>
is a subtype ofF<U>
ifT
is a subtype ofU
. - Contravariance:
F<T>
is a subtype ofF<U>
ifU
is a subtype ofT
. - Invariance:
F<T>
is never a subtype ofF<U>
(unlessT = U
).
Rust is usually able to infer the variance of a type parameter from its use,
but fails if the type parameter is not used within the type definition.
Typically, this is resolved by using a PhantomData
to indicate the
parameter’s use within the type:
use std::marker::PhantomData;
struct Slice<'a, T: 'a> {
start: *const T,
end: *const T,
phantom: PhantomData<&'a T>,
}
However, in some cases, the subtyping relation is not always obvious from a
PhantomData
field. In such cases, it can be useful to make the variance
explicit with one of the markers Covariant
, Contravariant
, and
Invariant
.
use type_variance::{Covariant, Contravariant};
struct Func<Arg, Ret> {
arg: Covariant<Arg>,
ret: Contravariant<Ret>,
}
§Enforcing invariance
Another use case is when a type parameter is used, but the Rust compiler
deduces a more permissive variance than is desired. In this case, the
Invariant
marker can be used to ensure that the generic type is invariant
with respect to the given type parameter.
use type_variance::Invariant;
struct Opaque<T> {
inner: Box<T>, // Implies `Opaque` is covariant to `T`
marker: Invariant<T>, // Ensures that `Opaque` is invariant to `T`
}
The Invariant
overrules any other implied variances and so Opaque
becomes invariant to T
.
§Lifetime parameters
Like PhantomData
, the provided variance markers only accept type
parameters. To indicate a generic type’s variance with respect to its
lifetime parameters, use the Lifetime
wrapper, which converts a
lifetime to a regular type while preserving its subtyping relation.
§Limitations
The marker traits Covariant
and Contravariant
do not necessarily
guarantee that the compiler will use the marked variance. If two uses of a
type parameter imply differing variances, the compiler will consider the
generic type invariant with respect to the parameter.
For example:
struct Ref<'a, T> {
inner: &'a T, // Implies `Ref` is covariant to `T`
marker: Contravariant<T>, // Implies `Ref` is contravariant to `T`
}
As a result of these conflicting variances, the compiler will decide that
Ref
is invariant to T
.
Due to this, it is recommended that Covariant
and Contravariant
are only
used on type parameters that are not used in any other fields of the type.
Structs§
- Contravariant
- Zero-sized type used to mark a type as contravariant with respect to its type
parameter
T
. - Covariant
- Zero-sized type used to mark a type as covariant with respect to its type
parameter
T
. - Invariant
- Zero-sized type used to mark a type as invariant with respect to its type
parameter
T
. - Lifetime
- Variance-preserving type wrapper around a lifetime parameter.
Traits§
- Variance
- A sealed trait implemented by
Covariant<T>
,Contravariant<T>
, andInvariant<T>
.