-
Notifications
You must be signed in to change notification settings - Fork 506
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document variance #186
Document variance #186
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
|
@@ -15,5 +15,75 @@ 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 the lifetime parameter `'a`, `&'static str` is a | ||
subtype of `&'a str`. | ||
|
||
[Higher-ranked] [function pointers] and [trait objects] have another | ||
subtype relation. They are subtypes of types that are given by substitutions of | ||
the higher-ranked lifetimes. Some examples: | ||
|
||
```rust | ||
// Here 'a is substituted for 'static | ||
let subtype: &(for<'a> fn(&'a i32) -> &'a i32) = &((|x| x) as fn(&_) -> &_); | ||
let supertype: &(fn(&'static i32) -> &'static i32) = subtype; | ||
|
||
// This works similarly for trait objects | ||
let subtype: &(for<'a> Fn(&'a i32) -> &'a i32) = &|x| x; | ||
let supertype: &(Fn(&'static i32) -> &'static i32) = subtype; | ||
|
||
// We can also substitute one higher-ranked lifetime for another | ||
let subtype: &(for<'a, 'b> fn(&'a i32, &'b i32))= &((|x, y| {}) as fn(&_, &_)); | ||
let supertype: &for<'c> fn(&'c i32, &'c i32) = subtype; | ||
``` | ||
|
||
## 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 | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thing I don't know but someone should check: is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like it is invariant, but there is a coercion from trait A {}
// This compiles (also with `*mut`, or `Box<RefCell>`)
fn coercion<'a, 'b>(x: &'b mut (A + 'static)) -> &'b mut (A + 'a) {
x
}
// This does not
fn not_subtype<'a, 'b, 'c>(x: &'c &'b mut (A + 'static)) -> &'c &'b mut (A + 'a) {
x
} |
||
|
||
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, // This makes `Variance` covariant in 'a, and would | ||
// make it covariant in U, but U is used later | ||
y: *const T, // Covariant in T | ||
z: UnsafeCell<&'b f64>, // Invariant in 'b | ||
w: *mut U, // Invariant in U, makes the whole struct invariant | ||
} | ||
``` | ||
|
||
[coercions]: type-coercions.html | ||
[function pointers]: types.html#function-pointer-types | ||
[Higher-ranked]: ../nomicon/hrtb.html | ||
[lifetime bound]: types.html#trait-object-lifetime-bounds | ||
[trait objects]: types.html#trait-objects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this escaped space necessary? otherwise markdown parses it as a reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Markdown would read it as a link with text of "[Higher ranked]" and a link reference to "function pointers" instead of as two separate links.