Skip to content

Commit

Permalink
Document variance
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed Jan 5, 2018
1 parent 6871424 commit 522e767
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
- [Dynamically Sized Types](dynamically-sized-types.md)
- [Type layout](type-layout.md)
- [Interior mutability](interior-mutability.md)
- [Subtyping](subtyping.md)
- [Subtyping and Variance](subtyping.md)
- [Type coercions](type-coercions.md)
- [Destructors](destructors.md)

Expand Down
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
71 changes: 68 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,70 @@ 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] function pointer and trait object
types. Replacing a higher ranked lifetime with a concrete lifetime produces a
subtype.

```rust
fn fun_ptr() {
// Explicitly f: &(for<'a> fn(&'a i32) -> &'a i32).
let f: &(fn(&i32) -> &i32) = &((|x| x) as fn(&i32) -> &i32);
let g: &(fn(&'static i32) -> &'static i32) = f;
}

fn fun_trait() {
// Explicitly f: &(for<'a> Fn(&'a i32) -> &'a i32).
let f: &(Fn(&i32) -> &i32) = &(|x| x);
let g: &(Fn(&'static i32) -> &'static i32) = f;
}
```

## 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

| Type | Variance in `'a` | Variance in `T` |
|-------------------------------|-------------------|-------------------|
| `&'a T` | covariant | covariant |
| `&'a mut T` | covariant | invariant |
| `*const T` | | covariant |
| `*mut T` | | invariant |
| `[T]` and `[T; n]` | | covariant |
| `fn() -> T` | | covariant |
| `fn(T) -> ()` | | contravariant |
| `std::cell::UnsafeCell<T>` | | invariant |
| `std::marker::PhantomData<T>` | | covariant |
| `Trait<T> + 'a` | covariant | invariant |

The variance of other `struct`, `enum`, `union` and tuple types is decided by
looking at the variance of the types of their fields. If the parameter is used
in positions with different variances then the parameter is invariant. 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]: ../nomicon/hrtb.html
[lifetime bound]: types.html#trait-object-lifetime-bounds

0 comments on commit 522e767

Please sign in to comment.