[][src]Crate type_variance

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 of F<U> if T is a subtype of U.
  • Contravariance: F<T> is a subtype of F<U> if U is a subtype of T.
  • Invariance: F<T> is never a subtype of F<U> (unless T = 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>, and Invariant<T>.

Functions

variance

A convenience function for constructing any of Covariant<T>, Contravariant<T>, and Invariant<T>. It is equivalent to default.