From 1427232800d6ac1857170f3a178ba519efe82604 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 30 Dec 2017 17:19:50 +0000 Subject: [PATCH] Document variance --- src/special-types-and-traits.md | 2 +- src/subtyping.md | 72 +++++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/special-types-and-traits.md b/src/special-types-and-traits.md index 497eceb3d..51815c31e 100644 --- a/src/special-types-and-traits.md +++ b/src/special-types-and-traits.md @@ -150,4 +150,4 @@ compiler, not by [implementation items]. [trait object]: types.html#trait-objects [Tuples]: types.html#tuple-types [Type parameters]: types.html#type-parameters -[variance]: ../nomicon/subtyping.html +[variance]: subtyping.html#variance diff --git a/src/subtyping.md b/src/subtyping.md index 4f133a360..bdb90afe6 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -1,4 +1,4 @@ -# Subtyping +# Subtyping and Variance Subtyping is implicit and can occur at any stage in type checking or inference. Subtyping in Rust is very restricted and occurs only due to @@ -15,5 +15,71 @@ fn bar<'a>() { let t: &'a str = s; } ``` -Since `'static` "lives longer" than `'a`, `&'static str` is a subtype of -`&'a str`. + +Since `'static` outlives `'a`, `&'static str` is a subtype of `&'a str`. + +Subtyping also exists for [higher-ranked types]. Replacing a higher ranked +lifetime with a concrete lifetime produces a subtype. + +```rust +fn bar() { + // Explicitly f: for<'a> fn(&'a i32) -> &'a i32. + let f: fn(&i32) -> &i32 = |x| x; + let g: fn(&'static i32) -> &'static i32 = f; +} +``` + +The subtype must be a valid type: + +```rust,compile_fail +fn bar<'a>() { + let h: for<'b, 'c> fn(&'c &'b i32) -> &'b i32 = |x| &**x; + let j: fn(&'static &'a i32) -> &'a i32 = h; + // Error: in type `&'static &'a i32`, reference has a longer lifetime than + // the data it references +} +``` + +## Variance + +Variance is a property that generic types have with respect to their arguments. +A generic type's *variance* in a parameter is how the subtyping of the +parameter affects the subtyping of the type. + +* `F` is *covariant* over `T` if `T` being a subtype of `U` implies that + `F` is a subtype of `F` (subtyping "passes through") +* `F` is *contravariant* over `T` if `T` being a subtype of `U` implies + that `F` is a subtype of `F` +* `F` is *invariant* over `T` otherwise (no subtyping relation can be derived) + +Variance of types is automatically determined as follows + +* `&'a T` is covariant in `'a` and `T`. +* `&'a mut T` is covariant in `'a`, invariant in `T`. +* `[T]` and `[T; n]` are covariant in `T`. +* `*const T` is covariant in `T`. +* `*mut T` is invariant in `T`. +* `fn(T) -> U` is covariant in `U` and contravariant in `T`. +* `std::cell::UnsafeCell` is invariant in `T`. +* `std::marker::PhantomData` is covariant in `T`. +* Trait objects are invariant in their type parameters and associated types and + covariant in their [lifetime bound]. + +The variance of `struct`, `enum`, `union` and tuple types is decided by looking +at the variance of the types of their fields. If a type parameter is used more +than once then the more restrictive variance is used. For example the following +struct is covariant in `'a` and `T` and invariant in `'b` and `U`. + +```rust +use std::cell::UnsafeCell; +struct Variance<'a, 'b, T, U: 'a> { + x: &'a U, + y: *const T, + z: UnsafeCell<&'b f64>, + w: *mut U, +} +``` + +[coercions]: type-coercions.html +[higher-ranked types]: ../nomicon/hrtb.html +[lifetime bound]: types.html#trait-object-lifetime-bounds