Skip to content

Commit 3e9eaec

Browse files
committed
---
yaml --- r: 229023 b: refs/heads/try c: d1b899e h: refs/heads/master i: 229021: dc78c75 229019: cc7249e 229015: 3873b83 229007: 74b4606 228991: 8ca3f99 v: v3
1 parent 93c1c3c commit 3e9eaec

File tree

2 files changed

+71
-50
lines changed

2 files changed

+71
-50
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
refs/heads/master: aca2057ed5fb7af3f8905b2bc01f72fa001c35c8
33
refs/heads/snap-stage3: 1af31d4974e33027a68126fa5a5a3c2c6491824f
4-
refs/heads/try: c5a1b87c6f95e023ab465514aac143bdbd88f56b
4+
refs/heads/try: d1b899e57a7aa5cec0380900465e0d7d443762d2
55
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
66
refs/tags/release-0.2: c870d2dffb391e14efb05aa27898f1f6333a9596
77
refs/tags/release-0.3: b5f0d0f648d9a6153664837026ba1be43d3e2503

branches/try/src/doc/tarpl/subtyping.md

Lines changed: 70 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,78 @@
11
% Subtyping and Variance
22

3-
Although Rust doesn't have any notion of inheritance, it *does* include subtyping.
4-
In Rust, subtyping derives entirely from *lifetimes*. Since lifetimes are scopes,
5-
we can partially order them based on the *contains* (outlives) relationship. We
6-
can even express this as a generic bound.
3+
Although Rust doesn't have any notion of inheritance, it *does* include
4+
subtyping. In Rust, subtyping derives entirely from *lifetimes*. Since lifetimes
5+
are scopes, we can partially order them based on the *contains* (outlives)
6+
relationship. We can even express this as a generic bound.
77

8-
Subtyping on lifetimes in terms of that relationship: if `'a: 'b`
9-
("a contains b" or "a outlives b"), then `'a` is a subtype of `'b`. This is a
10-
large source of confusion, because it seems intuitively backwards to many:
11-
the bigger scope is a *sub type* of the smaller scope.
8+
Subtyping on lifetimes in terms of that relationship: if `'a: 'b` ("a contains
9+
b" or "a outlives b"), then `'a` is a subtype of `'b`. This is a large source of
10+
confusion, because it seems intuitively backwards to many: the bigger scope is a
11+
*sub type* of the smaller scope.
1212

1313
This does in fact make sense, though. The intuitive reason for this is that if
14-
you expect an `&'a u8`, then it's totally fine for me to hand you an `&'static u8`,
15-
in the same way that if you expect an Animal in Java, it's totally fine for me to
16-
hand you a Cat. Cats are just Animals *and more*, just as `'static` is just `'a`
17-
*and more*.
14+
you expect an `&'a u8`, then it's totally fine for me to hand you an `&'static
15+
u8`, in the same way that if you expect an Animal in Java, it's totally fine for
16+
me to hand you a Cat. Cats are just Animals *and more*, just as `'static` is
17+
just `'a` *and more*.
1818

19-
(Note, the subtyping relationship and typed-ness of lifetimes is a fairly arbitrary
20-
construct that some disagree with. However it simplifies our analysis to treat
21-
lifetimes and types uniformly.)
19+
(Note, the subtyping relationship and typed-ness of lifetimes is a fairly
20+
arbitrary construct that some disagree with. However it simplifies our analysis
21+
to treat lifetimes and types uniformly.)
2222

23-
Higher-ranked lifetimes are also subtypes of every concrete lifetime. This is because
24-
taking an arbitrary lifetime is strictly more general than taking a specific one.
23+
Higher-ranked lifetimes are also subtypes of every concrete lifetime. This is
24+
because taking an arbitrary lifetime is strictly more general than taking a
25+
specific one.
2526

2627

2728

2829
# Variance
2930

3031
Variance is where things get a bit complicated.
3132

32-
Variance is a property that *type constructors* have. A type constructor in Rust
33-
is a generic type with unbound arguments. For instance `Vec` is a type constructor
34-
that takes a `T` and returns a `Vec<T>`. `&` and `&mut` are type constructors that
35-
take a two types: a lifetime, and a type to point to.
33+
Variance is a property that *type constructors* have with respect to their
34+
arguments. A type constructor in Rust is a generic type with unbound arguments.
35+
For instance `Vec` is a type constructor that takes a `T` and returns a
36+
`Vec<T>`. `&` and `&mut` are type constructors that take a two types: a
37+
lifetime, and a type to point to.
3638

3739
A type constructor's *variance* is how the subtyping of its inputs affects the
3840
subtyping of its outputs. There are two kinds of variance in Rust:
3941

40-
* F is *variant* if `T` being a subtype of `U` implies `F<T>` is a subtype of `F<U>`
41-
* F is *invariant* otherwise (no subtyping relation can be derived)
42+
* F is *variant* over `T` if `T` being a subtype of `U` implies
43+
`F<T>` is a subtype of `F<U>` (subtyping "passes through")
44+
* F is *invariant* over `T` otherwise (no subtyping relation can be derived)
4245

43-
(For those of you who are familiar with variance from other languages, what we refer
44-
to as "just" variance is in fact *covariance*. Rust does not have contravariance.
45-
Historically Rust did have some contravariance but it was scrapped due to poor
46-
interactions with other features.)
46+
(For those of you who are familiar with variance from other languages, what we
47+
refer to as "just" variance is in fact *covariance*. Rust does not have
48+
contravariance. Historically Rust did have some contravariance but it was
49+
scrapped due to poor interactions with other features. If you experience
50+
contravariance in Rust call your local compiler developer for medical advice.)
4751

4852
Some important variances:
4953

50-
* `&` is variant (as is `*const` by metaphor)
51-
* `&mut` is invariant
52-
* `Fn(T) -> U` is invariant with respect to `T`, but variant with respect to `U`
53-
* `Box`, `Vec`, and all other collections are variant
54-
* `UnsafeCell`, `Cell`, `RefCell`, `Mutex` and all "interior mutability"
55-
types are invariant (as is `*mut` by metaphor)
54+
* `&'a T` is variant over `'a` and `T` (as is `*const T` by metaphor)
55+
* `&'a mut T` is variant with over `'a` but invariant over `T`
56+
* `Fn(T) -> U` is invariant over `T`, but variant over `U`
57+
* `Box`, `Vec`, and all other collections are variant over their contents
58+
* `UnsafeCell<T>`, `Cell<T>`, `RefCell<T>`, `Mutex<T>` and all other
59+
interior mutability types are invariant over T (as is `*mut T` by metaphor)
5660

57-
To understand why these variances are correct and desirable, we will consider several
58-
examples. We have already covered why `&` should be variant when introducing subtyping:
59-
it's desirable to be able to pass longer-lived things where shorter-lived things are
60-
needed.
61+
To understand why these variances are correct and desirable, we will consider
62+
several examples.
6163

62-
To see why `&mut` should be invariant, consider the following code:
64+
65+
We have already covered why `&'a T` should be variant over `'a` when
66+
introducing subtyping: it's desirable to be able to pass longer-lived things
67+
where shorter-lived things are needed.
68+
69+
Similar reasoning applies to why it should be variant over T. It is reasonable
70+
to be able to pass `&&'static str` where an `&&'a str` is expected. The
71+
additional level of indirection does not change the desire to be able to pass
72+
longer lived things where shorted lived things are expected.
73+
74+
However this logic *does not* apply to see why `&mut`. To see why &mut should
75+
be invariant over T, consider the following code:
6376

6477
```rust,ignore
6578
fn overwrite<T: Copy>(input: &mut T, new: &mut T) {
@@ -78,17 +91,24 @@ fn main() {
7891
```
7992

8093
The signature of `overwrite` is clearly valid: it takes mutable references to
81-
two values of the same type, and overwrites one with the other. If `&mut` was
82-
variant, then `&mut &'a str` would be a subtype of `&mut &'static str`, since
83-
`&'a str` is a subtype of `&'static str`. Therefore the lifetime of
94+
two values of the same type, and overwrites one with the other. If `&mut T` was
95+
variant over T, then `&mut &'a str` would be a subtype of `&mut &'static str`,
96+
since `&'a str` is a subtype of `&'static str`. Therefore the lifetime of
8497
`forever_str` would successfully be "shrunk" down to the shorter lifetime of
8598
`string`, and `overwrite` would be called successfully. `string` would
8699
subsequently be dropped, and `forever_str` would point to freed memory when we
87100
print it! Therefore `&mut` should be invariant.
88101

89-
This is the general theme of variance vs
90-
invariance: if variance would allow you to *store* a short-lived value in a
91-
longer-lived slot, then you must be invariant.
102+
This is the general theme of variance vs invariance: if variance would allow you
103+
to *store* a short-lived value over a longer-lived slot, then you must be
104+
invariant.
105+
106+
However it *is* sound for `&'a mut T` to be variant over `'a`. The key difference
107+
between `'a` and T is that `'a` is a property of the reference itself,
108+
while T is something the reference is borrowing. If you change T's type, then
109+
the source still remembers the original type. However if you change the
110+
lifetime's type, no one but the reference knows this information, so it's fine.
111+
Put another way, `&'a mut T` owns `'a`, but only *borrows* T.
92112

93113
`Box` and `Vec` are interesting cases because they're variant, but you can
94114
definitely store values in them! This is where Rust gets really clever: it's
@@ -115,9 +135,9 @@ Weakening when you pass by-value is fine because there's no one else who
115135
trouble was because there's always someone else who remembers the original
116136
subtype: the actual owner.
117137

118-
The invariance of the cell types can be seen as follows: `&` is like an `&mut` for a
119-
cell, because you can still store values in them through an `&`. Therefore cells
120-
must be invariant to avoid lifetime smuggling.
138+
The invariance of the cell types can be seen as follows: `&` is like an `&mut`
139+
for a cell, because you can still store values in them through an `&`. Therefore
140+
cells must be invariant to avoid lifetime smuggling.
121141

122142
`Fn` is the most subtle case because it has mixed variance. To see why
123143
`Fn(T) -> U` should be invariant over T, consider the following function
@@ -128,8 +148,9 @@ signature:
128148
fn foo(&'a str) -> usize;
129149
```
130150

131-
This signature claims that it can handle any `&str` that lives *at least* as long
132-
as `'a`. Now if this signature was variant with respect to `&str`, that would mean
151+
This signature claims that it can handle any `&str` that lives *at least* as
152+
long as `'a`. Now if this signature was variant over `&'a str`, that
153+
would mean
133154

134155
```rust,ignore
135156
fn foo(&'static str) -> usize;

0 commit comments

Comments
 (0)