Skip to content

Commit 058602b

Browse files
committed
---
yaml --- r: 228993 b: refs/heads/try c: 5ec12b1 h: refs/heads/master i: 228991: 8ca3f99 v: v3
1 parent 4914719 commit 058602b

File tree

3 files changed

+57
-48
lines changed

3 files changed

+57
-48
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: adcd30c5a2aa732174b0c5deba3f4b9c9a9a44d0
4+
refs/heads/try: 5ec12b154bcc839f9a903f238791b6010a61418b
55
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
66
refs/tags/release-0.2: c870d2dffb391e14efb05aa27898f1f6333a9596
77
refs/tags/release-0.3: b5f0d0f648d9a6153664837026ba1be43d3e2503

branches/try/subtyping.md

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@
22

33
Although Rust doesn't have any notion of inheritance, it *does* include subtyping.
44
In Rust, subtyping derives entirely from *lifetimes*. Since lifetimes are scopes,
5-
we can partially order them based on a *contains* (outlives) relationship. We
6-
can even express this as a generic bound: `T: 'a` specifies that whatever scope `T`
7-
is valid for must contain the scope `'a` ("T outlives `'a`").
5+
we can partially order them based on the *contains* (outlives) relationship. We
6+
can even express this as a generic bound.
87

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

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

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

2223
Higher-ranked lifetimes are also subtypes of every concrete lifetime. This is because
2324
taking an arbitrary lifetime is strictly more general than taking a specific one.
@@ -26,15 +27,15 @@ taking an arbitrary lifetime is strictly more general than taking a specific one
2627

2728
# Variance
2829

29-
Variance is where things get really harsh.
30+
Variance is where things get a bit complicated.
3031

3132
Variance is a property that *type constructors* have. A type constructor in Rust
3233
is a generic type with unbound arguments. For instance `Vec` is a type constructor
3334
that takes a `T` and returns a `Vec<T>`. `&` and `&mut` are type constructors that
34-
take a lifetime and a type.
35+
take a two types: a lifetime, and a type to point to.
3536

36-
A type constructor's *variance* is how the subtypes of its inputs affects the
37-
subtypes of its outputs. There are three kinds of variance:
37+
A type constructor's *variance* is how the subtyping of its inputs affects the
38+
subtyping of its outputs. There are two kinds of variance in Rust:
3839

3940
* F is *variant* if `T` being a subtype of `U` implies `F<T>` is a subtype of `F<U>`
4041
* F is *invariant* otherwise (no subtyping relation can be derived)
@@ -60,42 +61,47 @@ needed.
6061

6162
To see why `&mut` should be invariant, consider the following code:
6263

63-
```rust
64+
```rust,ignore
65+
fn overwrite<T: Copy>(input: &mut T, new: &mut T) {
66+
*input = *new;
67+
}
68+
6469
fn main() {
6570
let mut forever_str: &'static str = "hello";
6671
{
6772
let string = String::from("world");
6873
overwrite(&mut forever_str, &mut &*string);
6974
}
75+
// Oops, printing free'd memory
7076
println!("{}", forever_str);
7177
}
72-
73-
fn overwrite<T: Copy>(input: &mut T, new: &mut T) {
74-
*input = *new;
75-
}
7678
```
7779

78-
The signature of `overwrite` is clearly valid: it takes mutable references to two values
79-
of the same type, and overwrites one with the other. We have seen already that `&` is
80-
variant, and `'static` is a subtype of *any* `'a`, so `&'static str` is a
81-
subtype of `&'a str`. Therefore, if `&mut` was
82-
*also* variant, then the lifetime of the `&'static str` would successfully be
83-
"shrunk" down to the shorter lifetime of the string, and `overwrite` would be
84-
called successfully. The string would subsequently be dropped, and `forever_str`
85-
would point to freed memory when we print it!
80+
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
84+
`forever_str` would successfully be "shrunk" down to the shorter lifetime of
85+
`string`, and `overwrite` would be called successfully. `string` would
86+
subsequently be dropped, and `forever_str` would point to freed memory when we
87+
print it! Therefore `&mut` should be invariant.
8688

87-
Therefore `&mut` should be invariant. This is the general theme of variance vs
89+
This is the general theme of variance vs
8890
invariance: if variance would allow you to *store* a short-lived value in a
8991
longer-lived slot, then you must be invariant.
9092

9193
`Box` and `Vec` are interesting cases because they're variant, but you can
92-
definitely store values in them! This is fine because *you can only store values
93-
in them through a mutable reference*! The mutable reference makes the whole type
94-
invariant, and therefore prevents you from getting in trouble.
94+
definitely store values in them! This is where Rust gets really clever: it's
95+
fine for them to be variant because you can only store values
96+
in them *via a mutable reference*! The mutable reference makes the whole type
97+
invariant, and therefore prevents you from smuggling a short-lived type into
98+
them.
99+
100+
Being variant *does* allows them to be weakened when shared immutably.
101+
So you can pass a `&Box<&'static str>` where a `&Box<&'a str>` is expected.
95102

96-
Being variant allows them to be variant when shared immutably (so you can pass
97-
a `&Box<&'static str>` where a `&Box<&'a str>` is expected). It also allows you to
98-
forever weaken the type by moving it into a weaker slot. That is, you can do:
103+
However what should happen when passing *by-value* is less obvious. It turns out
104+
that, yes, you can use subtyping when passing by-value. That is, this works:
99105

100106
```rust
101107
fn get_box<'a>(&'a u8) -> Box<&'a str> {
@@ -104,14 +110,16 @@ fn get_box<'a>(&'a u8) -> Box<&'a str> {
104110
}
105111
```
106112

107-
which is fine because unlike the mutable borrow case, there's no one else who
108-
"remembers" the old lifetime in the box.
113+
Weakening when you pass by-value is fine because there's no one else who
114+
"remembers" the old lifetime in the Box. The reason a variant `&mut` was
115+
trouble was because there's always someone else who remembers the original
116+
subtype: the actual owner.
109117

110-
The variance of the cell types similarly follows. `&` is like an `&mut` for a
118+
The invariance of the cell types can be seen as follows: `&` is like an `&mut` for a
111119
cell, because you can still store values in them through an `&`. Therefore cells
112120
must be invariant to avoid lifetime smuggling.
113121

114-
`Fn` is the most subtle case, because it has mixed variance. To see why
122+
`Fn` is the most subtle case because it has mixed variance. To see why
115123
`Fn(T) -> U` should be invariant over T, consider the following function
116124
signature:
117125

@@ -120,7 +128,7 @@ signature:
120128
fn foo(&'a str) -> usize;
121129
```
122130

123-
This signature claims that it can handle any &str that lives *at least* as long
131+
This signature claims that it can handle any `&str` that lives *at least* as long
124132
as `'a`. Now if this signature was variant with respect to `&str`, that would mean
125133

126134
```rust

branches/try/unwinding.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -263,17 +263,18 @@ impl<T: Ord> BinaryHeap<T> {
263263

264264
Although all unsafe code *must* ensure some minimal level of exception safety,
265265
some types may choose to explicitly *poison* themselves if they witness a panic.
266-
The most notable example of this is the standard library's Mutex type. A Mutex
267-
will poison itself if one of its MutexGuards (the thing it returns when a lock
268-
is obtained) is dropped during a panic. Any future attempts to lock the Mutex
269-
will return an `Err`.
266+
Poisoning doesn't entail anything in particular. Generally it just means
267+
preventing normal usage from proceeding. The most notable example of this is the
268+
standard library's Mutex type. A Mutex will poison itself if one of its
269+
MutexGuards (the thing it returns when a lock is obtained) is dropped during a
270+
panic. Any future attempts to lock the Mutex will return an `Err`.
270271

271272
Mutex poisons not for *true* safety in the sense that Rust normally cares about. It
272273
poisons as a safety-guard against blindly using the data that comes out of a Mutex
273274
that has witnessed a panic while locked. The data in such a Mutex was likely in the
274275
middle of being modified, and as such may be in an inconsistent or incomplete state.
275276
It is important to note that one cannot violate memory safety with such a type
276-
if it is correctly written. After all, it must be exception safe!
277+
if it is correctly written. After all, it must be minimally exception safe!
277278

278279
However if the Mutex contained, say, a BinaryHeap that does not actually have the
279280
heap property, it's unlikely that any code that uses it will do
@@ -288,7 +289,7 @@ the Err exposes a method to get the lock anyway. It *is* safe, after all.
288289
Rust's unwinding strategy is not specified to be fundamentally compatible
289290
with any other language's unwinding. As such, unwinding into Rust from another
290291
language, or unwinding into another language from Rust is Undefined Behaviour.
291-
What you do at that point is up to you, but you must *absolutely* catch any
292-
panics at the FFI boundary! At best, your application will crash and burn. At
293-
worst, your application *won't* crash and burn, and will proceed with completely
294-
clobbered state.
292+
You must *absolutely* catch any panics at the FFI boundary! What you do at that
293+
point is up to you, but *something* must be done. If you fail to do this,
294+
at best, your application will crash and burn. At worst, your application *won't*
295+
crash and burn, and will proceed with completely clobbered state.

0 commit comments

Comments
 (0)