Skip to content

Commit

Permalink
Document variance
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed Dec 30, 2017
1 parent 6871424 commit 1427232
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/special-types-and-traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
72 changes: 69 additions & 3 deletions src/subtyping.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<T>` is *covariant* over `T` if `T` being a subtype of `U` implies that
`F<T>` is a subtype of `F<U>` (subtyping "passes through")
* `F<T>` is *contravariant* over `T` if `T` being a subtype of `U` implies
that `F<U>` is a subtype of `F<T>`
* `F<T>` 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<T>` is invariant in `T`.
* `std::marker::PhantomData<T>` 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

0 comments on commit 1427232

Please sign in to comment.