Skip to content

Commit 23a57a0

Browse files
committed
---
yaml --- r: 236095 b: refs/heads/stable c: eba459a h: refs/heads/master i: 236093: f100c0d 236091: 8dd8fed 236087: ab71ad9 236079: b181dbf 236063: fa22405 236031: fc5c0b8 v: v3
1 parent ca5e937 commit 23a57a0

File tree

5 files changed

+358
-2
lines changed

5 files changed

+358
-2
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ refs/heads/tmp: afae2ff723393b3ab4ccffef6ac7c6d1809e2da0
2929
refs/tags/1.0.0-alpha.2: 4c705f6bc559886632d3871b04f58aab093bfa2f
3030
refs/tags/homu-tmp: f859507de8c410b648d934d8f5ec1c52daac971d
3131
refs/tags/1.0.0-beta: 8cbb92b53468ee2b0c2d3eeb8567005953d40828
32-
refs/heads/stable: d1b899e57a7aa5cec0380900465e0d7d443762d2
32+
refs/heads/stable: eba459a5c2abf1c6869f0947e297b0805cdc27ef
3333
refs/tags/1.0.0: 55bd4f8ff2b323f317ae89e254ce87162d52a375
3434
refs/tags/1.1.0: bc3c16f09287e5545c1d3f76b7abd54f2eca868b
3535
refs/tags/1.2.0: f557861f822c34f07270347b94b5280de20a597e

branches/stable/src/doc/tarpl/SUMMARY.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
* [Unbounded Lifetimes](unbounded-lifetimes.md)
1616
* [Higher-Rank Trait Bounds](hrtb.md)
1717
* [Subtyping and Variance](subtyping.md)
18-
* [Misc](lifetime-misc.md)
18+
* [Drop Check](dropck.md)
19+
* [PhantomData](phantom-data.md)
20+
* [Splitting Lifetimes](lifetime-splitting.md)
1921
* [Type Conversions](conversions.md)
2022
* [Coercions](coercions.md)
2123
* [The Dot Operator](dot-operator.md)
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
% Drop Check
2+
3+
We have seen how lifetimes provide us some fairly simple rules for ensuring
4+
that never read dangling references. However up to this point we have only ever
5+
interacted with the *outlives* relationship in an inclusive manner. That is,
6+
when we talked about `'a: 'b`, it was ok for `'a` to live *exactly* as long as
7+
`'b`. At first glance, this seems to be a meaningless distinction. Nothing ever
8+
gets dropped at the same time as another, right? This is why we used the
9+
following desugarring of `let` statements:
10+
11+
```rust
12+
let x;
13+
let y;
14+
```
15+
16+
```rust
17+
{
18+
let x;
19+
{
20+
let y;
21+
}
22+
}
23+
```
24+
25+
Each creates its own scope, clearly establishing that one drops before the
26+
other. However, what if we do the following?
27+
28+
```rust
29+
let (x, y) = (vec![], vec![]);
30+
```
31+
32+
Does either value strictly outlive the other? The answer is in fact *no*,
33+
neither value strictly outlives the other. Of course, one of x or y will be
34+
dropped before the other, but the actual order is not specified. Tuples aren't
35+
special in this regard; composite structures just don't guarantee their
36+
destruction order as of Rust 1.0.
37+
38+
We *could* specify this for the fields of built-in composites like tuples and
39+
structs. However, what about something like Vec? Vec has to manually drop its
40+
elements via pure-library code. In general, anything that implements Drop has
41+
a chance to fiddle with its innards during its final death knell. Therefore
42+
the compiler can't sufficiently reason about the actual destruction order
43+
of the contents of any type that implements Drop.
44+
45+
So why do we care? We care because if the type system isn't careful, it could
46+
accidentally make dangling pointers. Consider the following simple program:
47+
48+
```rust
49+
struct Inspector<'a>(&'a u8);
50+
51+
fn main() {
52+
let (days, inspector);
53+
days = Box::new(1);
54+
inspector = Inspector(&days);
55+
}
56+
```
57+
58+
This program is totally sound and compiles today. The fact that `days` does
59+
not *strictly* outlive `inspector` doesn't matter. As long as the `inspector`
60+
is alive, so is days.
61+
62+
However if we add a destructor, the program will no longer compile!
63+
64+
```rust,ignore
65+
struct Inspector<'a>(&'a u8);
66+
67+
impl<'a> Drop for Inspector<'a> {
68+
fn drop(&mut self) {
69+
println!("I was only {} days from retirement!", self.0);
70+
}
71+
}
72+
73+
fn main() {
74+
let (days, inspector);
75+
days = Box::new(1);
76+
inspector = Inspector(&days);
77+
// Let's say `days` happens to get dropped first.
78+
// Then when Inspector is dropped, it will try to read free'd memory!
79+
}
80+
```
81+
82+
```text
83+
<anon>:12:28: 12:32 error: `days` does not live long enough
84+
<anon>:12 inspector = Inspector(&days);
85+
^~~~
86+
<anon>:9:11: 15:2 note: reference must be valid for the block at 9:10...
87+
<anon>:9 fn main() {
88+
<anon>:10 let (days, inspector);
89+
<anon>:11 days = Box::new(1);
90+
<anon>:12 inspector = Inspector(&days);
91+
<anon>:13 // Let's say `days` happens to get dropped first.
92+
<anon>:14 // Then when Inspector is dropped, it will try to read free'd memory!
93+
...
94+
<anon>:10:27: 15:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 10:26
95+
<anon>:10 let (days, inspector);
96+
<anon>:11 days = Box::new(1);
97+
<anon>:12 inspector = Inspector(&days);
98+
<anon>:13 // Let's say `days` happens to get dropped first.
99+
<anon>:14 // Then when Inspector is dropped, it will try to read free'd memory!
100+
<anon>:15 }
101+
```
102+
103+
Implementing Drop lets the Inspector execute some arbitrary code *during* its
104+
death. This means it can potentially observe that types that are supposed to
105+
live as long as it does actually were destroyed first.
106+
107+
Interestingly, only *generic* types need to worry about this. If they aren't
108+
generic, then the only lifetimes they can harbor are `'static`, which will truly
109+
live *forever*. This is why this problem is referred to as *sound generic drop*.
110+
Sound generic drop is enforced by the *drop checker*. As of this writing, some
111+
of the finer details of how the drop checker validates types is totally up in
112+
the air. However The Big Rule is the subtlety that we have focused on this whole
113+
section:
114+
115+
**For a generic type to soundly implement drop, it must strictly outlive all of
116+
its generic arguments.**
117+
118+
This rule is sufficient but not necessary to satisfy the drop checker. That is,
119+
if your type obeys this rule then it's *definitely* sound to drop. However
120+
there are special cases where you can fail to satisfy this, but still
121+
successfully pass the borrow checker. These are the precise rules that are
122+
currently up in the air.
123+
124+
It turns out that when writing unsafe code, we generally don't need to
125+
worry at all about doing the right thing for the drop checker. However there
126+
is *one* special case that you need to worry about, which we will look at in
127+
the next section.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
% Splitting Lifetimes
2+
3+
The mutual exclusion property of mutable references can be very limiting when
4+
working with a composite structure. The borrow checker understands some basic stuff, but
5+
will fall over pretty easily. It *does* understand structs sufficiently to
6+
know that it's possible to borrow disjoint fields of a struct simultaneously.
7+
So this works today:
8+
9+
```rust
10+
struct Foo {
11+
a: i32,
12+
b: i32,
13+
c: i32,
14+
}
15+
16+
let mut x = Foo {a: 0, b: 0, c: 0};
17+
let a = &mut x.a;
18+
let b = &mut x.b;
19+
let c = &x.c;
20+
*b += 1;
21+
let c2 = &x.c;
22+
*a += 10;
23+
println!("{} {} {} {}", a, b, c, c2);
24+
```
25+
26+
However borrowck doesn't understand arrays or slices in any way, so this doesn't
27+
work:
28+
29+
```rust,ignore
30+
let x = [1, 2, 3];
31+
let a = &mut x[0];
32+
let b = &mut x[1];
33+
println!("{} {}", a, b);
34+
```
35+
36+
```text
37+
<anon>:3:18: 3:22 error: cannot borrow immutable indexed content `x[..]` as mutable
38+
<anon>:3 let a = &mut x[0];
39+
^~~~
40+
<anon>:4:18: 4:22 error: cannot borrow immutable indexed content `x[..]` as mutable
41+
<anon>:4 let b = &mut x[1];
42+
^~~~
43+
error: aborting due to 2 previous errors
44+
```
45+
46+
While it was plausible that borrowck could understand this simple case, it's
47+
pretty clearly hopeless for borrowck to understand disjointness in general
48+
container types like a tree, especially if distinct keys actually *do* map
49+
to the same value.
50+
51+
In order to "teach" borrowck that what we're doing is ok, we need to drop down
52+
to unsafe code. For instance, mutable slices expose a `split_at_mut` function that
53+
consumes the slice and returns *two* mutable slices. One for everything to the
54+
left of the index, and one for everything to the right. Intuitively we know this
55+
is safe because the slices don't alias. However the implementation requires some
56+
unsafety:
57+
58+
```rust,ignore
59+
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
60+
unsafe {
61+
let self2: &mut [T] = mem::transmute_copy(&self);
62+
63+
(ops::IndexMut::index_mut(self, ops::RangeTo { end: mid } ),
64+
ops::IndexMut::index_mut(self2, ops::RangeFrom { start: mid } ))
65+
}
66+
}
67+
```
68+
69+
This is pretty plainly dangerous. We use transmute to duplicate the slice with an
70+
*unbounded* lifetime, so that it can be treated as disjoint from the other until
71+
we unify them when we return.
72+
73+
However more subtle is how iterators that yield mutable references work.
74+
The iterator trait is defined as follows:
75+
76+
```rust
77+
trait Iterator {
78+
type Item;
79+
80+
fn next(&mut self) -> Option<Self::Item>;
81+
}
82+
```
83+
84+
Given this definition, Self::Item has *no* connection to `self`. This means
85+
that we can call `next` several times in a row, and hold onto all the results
86+
*concurrently*. This is perfectly fine for by-value iterators, which have exactly
87+
these semantics. It's also actually fine for shared references, as they admit
88+
arbitrarily many references to the same thing (although the
89+
iterator needs to be a separate object from the thing being shared). But mutable
90+
references make this a mess. At first glance, they might seem completely
91+
incompatible with this API, as it would produce multiple mutable references to
92+
the same object!
93+
94+
However it actually *does* work, exactly because iterators are one-shot objects.
95+
Everything an IterMut yields will be yielded *at most* once, so we don't *actually*
96+
ever yield multiple mutable references to the same piece of data.
97+
98+
In general all mutable iterators require *some* unsafe code *somewhere*, though.
99+
Whether it's raw pointers, or safely composing on top of *another* IterMut.
100+
101+
For instance, VecDeque's IterMut:
102+
103+
```rust,ignore
104+
struct IterMut<'a, T:'a> {
105+
// The whole backing array. Some of these indices are initialized!
106+
ring: &'a mut [T],
107+
tail: usize,
108+
head: usize,
109+
}
110+
111+
impl<'a, T> Iterator for IterMut<'a, T> {
112+
type Item = &'a mut T;
113+
114+
fn next(&mut self) -> Option<&'a mut T> {
115+
if self.tail == self.head {
116+
return None;
117+
}
118+
let tail = self.tail;
119+
self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len());
120+
121+
unsafe {
122+
// might as well do unchecked indexing since wrap_index has us
123+
// in-bounds, and many of the "middle" indices are uninitialized
124+
// anyway.
125+
let elem = self.ring.get_unchecked_mut(tail);
126+
127+
// round-trip through a raw pointer to unbound the lifetime from
128+
// ourselves
129+
Some(&mut *(elem as *mut _))
130+
}
131+
}
132+
}
133+
```
134+
135+
A very subtle but interesting detail in this design is that it *relies on
136+
privacy to be sound*. Borrowck works on some very simple rules. One of those rules
137+
is that if we have a live &mut Foo and Foo contains an &mut Bar, then that &mut
138+
Bar is *also* live. Since IterMut is always live when `next` can be called, if
139+
`ring` were public then we could mutate `ring` while outstanding mutable borrows
140+
to it exist!
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
% PhantomData
2+
3+
When working with unsafe code, we can often end up in a situation where
4+
types or lifetimes are logically associated with a struct, but not actually
5+
part of a field. This most commonly occurs with lifetimes. For instance, the
6+
`Iter` for `&'a [T]` is (approximately) defined as follows:
7+
8+
```rust,ignore
9+
struct Iter<'a, T: 'a> {
10+
ptr: *const T,
11+
end: *const T,
12+
}
13+
```
14+
15+
However because `'a` is unused within the struct's body, it's *unbounded*.
16+
Because of the troubles this has historically caused, unbounded lifetimes and
17+
types are *illegal* in struct definitions. Therefore we must somehow refer
18+
to these types in the body. Correctly doing this is necessary to have
19+
correct variance and drop checking.
20+
21+
We do this using *PhantomData*, which is a special marker type. PhantomData
22+
consumes no space, but simulates a field of the given type for the purpose of
23+
static analysis. This was deemed to be less error-prone than explicitly telling
24+
the type-system the kind of variance that you want, while also providing other
25+
useful such as the information needed by drop check.
26+
27+
Iter logically contains a bunch of `&'a T`s, so this is exactly what we tell
28+
the PhantomData to simulate:
29+
30+
```
31+
use std::marker;
32+
33+
struct Iter<'a, T: 'a> {
34+
ptr: *const T,
35+
end: *const T,
36+
_marker: marker::PhantomData<&'a T>,
37+
}
38+
```
39+
40+
and that's it. The lifetime will be bounded, and your iterator will be variant
41+
over `'a` and `T`. Everything Just Works.
42+
43+
Another important example is Vec, which is (approximately) defined as follows:
44+
45+
```
46+
struct Vec<T> {
47+
data: *const T, // *const for variance!
48+
len: usize,
49+
cap: usize,
50+
}
51+
```
52+
53+
Unlike the previous example it *appears* that everything is exactly as we
54+
want. Every generic argument to Vec shows up in the at least one field.
55+
Good to go!
56+
57+
Nope.
58+
59+
The drop checker will generously determine that Vec<T> does not own any values
60+
of type T. This will in turn make it conclude that it does *not* need to worry
61+
about Vec dropping any T's in its destructor for determining drop check
62+
soundness. This will in turn allow people to create unsoundness using
63+
Vec's destructor.
64+
65+
In order to tell dropck that we *do* own values of type T, and therefore may
66+
drop some T's when *we* drop, we must add an extra PhantomData saying exactly
67+
that:
68+
69+
```
70+
use std::marker;
71+
72+
struct Vec<T> {
73+
data: *const T, // *const for covariance!
74+
len: usize,
75+
cap: usize,
76+
_marker: marker::PhantomData<T>,
77+
}
78+
```
79+
80+
Raw pointers that own an allocation is such a pervasive pattern that the
81+
standard library made a utility for itself called `Unique<T>` which:
82+
83+
* wraps a `*const T` for variance
84+
* includes a `PhantomData<T>`,
85+
* auto-derives Send/Sync as if T was contained
86+
* marks the pointer as NonZero for the null-pointer optimization
87+

0 commit comments

Comments
 (0)