type_variance/lib.rs
1#![no_std]
2
3//! Marker types to indicate precisely the [variance] relationship between a
4//! generic type and its parameters.
5//!
6//! Rust supports three different modes of variance between a generic type `F`
7//! and a type parameter `T`:<sup>[1]</sup>
8//! - Covariance: `F<T>` is a subtype of `F<U>` if `T` is a subtype of `U`.
9//! - Contravariance: `F<T>` is a subtype of `F<U>` if `U` is a subtype of `T`.
10//! - Invariance: `F<T>` is never a subtype of `F<U>` (unless `T = U`).
11//!
12//! Rust is usually able to infer the variance of a type parameter from its use,
13//! but fails if the type parameter is not used within the type definition.
14//! Typically, this is resolved by using a [`PhantomData`] to indicate the
15//! parameter's use within the type:
16//! ```
17//! use std::marker::PhantomData;
18//!
19//! struct Slice<'a, T: 'a> {
20//! start: *const T,
21//! end: *const T,
22//! phantom: PhantomData<&'a T>,
23//! }
24//! ```
25//!
26//! However, in some cases, the subtyping relation is not always obvious from a
27//! `PhantomData` field. In such cases, it can be useful to make the variance
28//! explicit with one of the markers [`Covariant`], [`Contravariant`], and
29//! [`Invariant`].
30//! ```
31//! use type_variance::{Covariant, Contravariant};
32//!
33//! struct Func<Arg, Ret> {
34//! arg: Covariant<Arg>,
35//! ret: Contravariant<Ret>,
36//! }
37//! ```
38//!
39//! ## Enforcing invariance
40//!
41//! Another use case is when a type parameter is used, but the Rust compiler
42//! deduces a more permissive variance than is desired. In this case, the
43//! `Invariant` marker can be used to ensure that the generic type is invariant
44//! with respect to the given type parameter.
45//! ```
46//! use type_variance::Invariant;
47//!
48//! struct Opaque<T> {
49//! inner: Box<T>, // Implies `Opaque` is covariant to `T`
50//! marker: Invariant<T>, // Ensures that `Opaque` is invariant to `T`
51//! }
52//! ```
53//! The `Invariant` overrules any other implied variances and so `Opaque`
54//! becomes invariant to `T`.
55//!
56//! ## Lifetime parameters
57//!
58//! Like `PhantomData`, the provided variance markers only accept type
59//! parameters. To indicate a generic type's variance with respect to its
60//! lifetime parameters, use the [`Lifetime`] wrapper, which converts a
61//! lifetime to a regular type while preserving its subtyping relation.
62//!
63//! # Limitations
64//!
65//! The marker traits `Covariant` and `Contravariant` _do not_ necessarily
66//! guarantee that the compiler will use the marked variance. If two uses of a
67//! type parameter imply differing variances, the compiler will consider the
68//! generic type _invariant_ with respect to the parameter.
69//!
70//! For example:
71//! ```
72//! # use type_variance::Contravariant;
73//! #
74//! struct Ref<'a, T> {
75//! inner: &'a T, // Implies `Ref` is covariant to `T`
76//! marker: Contravariant<T>, // Implies `Ref` is contravariant to `T`
77//! }
78//! ```
79//! As a result of these conflicting variances, the compiler will decide that
80//! `Ref` is invariant to `T`.
81//!
82//! Due to this, it is recommended that `Covariant` and `Contravariant` are only
83//! used on type parameters that are not used in any other fields of the type.
84//!
85//! [variance]: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
86//! [1]: https://doc.rust-lang.org/nomicon/subtyping.html#variance
87//! [`PhantomData`]: https://doc.rust-lang.org/stable/std/marker/struct.PhantomData.html
88//! [`Covariant`]: struct.Covariant.html
89//! [`Contravariant`]: struct.Contravariant.html
90//! [`Invariant`]: struct.Invariant.html
91//! [`Lifetime`]: struct.Lifetime.html
92
93use core::marker::PhantomData;
94
95/// A sealed trait implemented by `Covariant<T>`, `Contravariant<T>`, and
96/// `Invariant<T>`.
97pub trait Variance: Default + private::Sealed {}
98
99/// Zero-sized type used to mark a type as [covariant] with respect to its type
100/// parameter `T`.
101///
102/// [covariant]: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
103///
104/// See the [module-level documentation](index.html) for more.
105#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
106pub struct Covariant<T: ?Sized> {
107 marker: PhantomData<fn() -> T>,
108}
109
110/// Zero-sized type used to mark a type as [contravariant] with respect to its type
111/// parameter `T`.
112///
113/// [contravariant]: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
114///
115/// See the [module-level documentation](index.html) for more.
116#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
117pub struct Contravariant<T: ?Sized> {
118 marker: PhantomData<fn(T)>,
119}
120
121/// Zero-sized type used to mark a type as [invariant] with respect to its type
122/// parameter `T`.
123///
124/// [invariant]: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
125///
126/// See the [module-level documentation](index.html) for more.
127#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
128pub struct Invariant<T: ?Sized> {
129 marker: (Covariant<T>, Contravariant<T>),
130}
131
132// NOTE: These manual impls are necessary due to the following issue, but
133// can be replaced with a #[derive(Default)] when/if it gets resolved.
134// https://github.com/rust-lang/rust/issues/26925
135impl<T: ?Sized> Default for Covariant<T> {
136 fn default() -> Self {
137 Self { marker: Default::default(), }
138 }
139}
140impl<T: ?Sized> Default for Contravariant<T> {
141 fn default() -> Self {
142 Self { marker: Default::default(), }
143 }
144}
145impl<T: ?Sized> Default for Invariant<T> {
146 fn default() -> Self {
147 Self { marker: Default::default(), }
148 }
149}
150
151impl<T: ?Sized> private::Sealed for Covariant<T> {}
152impl<T: ?Sized> private::Sealed for Contravariant<T> {}
153impl<T: ?Sized> private::Sealed for Invariant<T> {}
154
155impl<T: ?Sized> Variance for Covariant<T> {}
156impl<T: ?Sized> Variance for Contravariant<T> {}
157impl<T: ?Sized> Variance for Invariant<T> {}
158
159/// Variance-preserving type wrapper around a lifetime parameter.
160///
161/// This can be useful to indicate the variance of a generic type with respect
162/// to a lifetime parameter, rather than a type parameter.
163///
164/// For example:
165/// ```
166/// use type_variance::{Covariant, Lifetime};
167///
168/// struct Guard<'a> {
169/// marker: Covariant<Lifetime<'a>>,
170/// }
171/// ```
172/// This marks `Guard` as being covariant to `'a`.
173///
174/// Note that this type is not constructible, and so should be wrapped with
175/// either a `PhantomData` or one of the variance marker types.
176#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
177pub struct Lifetime<'a> {
178 _variance: Covariant<&'a ()>,
179}
180
181/// A convenience function for constructing any of `Covariant<T>`,
182/// `Contravariant<T>`, and `Invariant<T>`. It is equivalent to [`default`].
183///
184/// [`default`]: https://doc.rust-lang.org/stable/std/default/trait.Default.html#tymethod.default
185///
186/// For example:
187/// ```
188/// use type_variance::{Covariant, variance};
189///
190/// struct Co<T> {
191/// other_data: u32,
192/// marker: Covariant<T>,
193/// }
194///
195/// impl<T> Co<T> {
196/// fn new() -> Self {
197/// Co {
198/// other_data: 42,
199/// marker: variance(),
200/// }
201/// }
202/// }
203/// ```
204pub fn variance<T: Variance>() -> T {
205 Default::default()
206}
207
208// Prevent external implementations of `Variance`.
209mod private {
210 pub trait Sealed {}
211}
212
213#[cfg(test)]
214mod tests {
215 use super::{Covariant, Contravariant, Lifetime};
216
217 struct Co<X>(Covariant<X>);
218 struct Contra<X>(Contravariant<X>);
219
220 #[test]
221 fn covariant<'a>() {
222 let _co: Co<Lifetime<'a>> = Co(
223 Covariant::<Lifetime<'static>>::default(),
224 );
225 }
226
227 #[test]
228 fn contravariant<'a>() {
229 let _contra: Contra<Lifetime<'static>> = Contra(
230 Contravariant::<Lifetime<'a>>::default(),
231 );
232 }
233
234 #[test]
235 fn co_contra<'a>() {
236 struct Func<Arg, Ret> {
237 _arg: Covariant<Arg>,
238 _ret: Contravariant<Ret>,
239 }
240 let _func: Func<Lifetime<'a>, Lifetime<'static>> = Func {
241 _arg: Covariant::<Lifetime<'static>>::default(),
242 _ret: Contravariant::<Lifetime<'a>>::default(),
243 };
244 }
245}