Skip to content

Commit 8e27be8

Browse files
committed
---
yaml --- r: 236019 b: refs/heads/stable c: 74b398b h: refs/heads/master i: 236017: fb8bf3b 236015: 6a94029 v: v3
1 parent 9a44918 commit 8e27be8

File tree

2 files changed

+121
-13
lines changed

2 files changed

+121
-13
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: 02134464b0ac3cb64e221fd008b8b9e6bad1d932
32+
refs/heads/stable: 74b398b93d647237acbc1c738e55e47ec13ea7b2
3333
refs/tags/1.0.0: 55bd4f8ff2b323f317ae89e254ce87162d52a375
3434
refs/tags/1.1.0: bc3c16f09287e5545c1d3f76b7abd54f2eca868b
3535
refs/tags/1.2.0: f557861f822c34f07270347b94b5280de20a597e

branches/stable/lifetimes.md

Lines changed: 120 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
% Ownership
1+
% Ownership and Lifetimes
22

33
Ownership is the breakout feature of Rust. It allows Rust to be completely
44
memory-safe and efficient, while avoiding garbage collection. Before getting
@@ -58,6 +58,13 @@ a Cell is to copy the bits in or out.
5858
manages this through *runtime* checks. It is effectively a thread-unsafe
5959
read-write lock.
6060

61+
For more details see Dan Grossman's Existential Types for Imperative Languages:
62+
* [paper][grossman-paper] (Advanced)
63+
* [slides][grossman-slides] (Simple)
64+
65+
[grossman-paper]: http://homes.cs.washington.edu/~djg/papers/exists_imp.pdf
66+
[grossman-slides]: https://homes.cs.washington.edu/~djg/slides/esop02_talk.pdf
67+
6168

6269

6370

@@ -96,19 +103,49 @@ more than a local lint against incorrect usage of a value.
96103

97104
## Weird Lifetimes
98105

99-
Almost always, the mutability of a lifetime can be derived from the mutability
100-
of the reference it is attached to. However this is not necessarily the case.
101-
For instance in the following code:
106+
Given the following code:
102107

103108
```rust
104-
fn foo<'a>(input: &'a mut u8) -> &'a u8 { &* input }
109+
struct Foo;
110+
111+
impl Foo {
112+
fn mutate_and_share(&mut self) -> &Self { &*self }
113+
fn share(&self) {}
114+
}
115+
116+
fn main() {
117+
let mut foo = Foo;
118+
let loan = foo.mutate_and_share();
119+
foo.share();
120+
}
121+
```
122+
123+
One might expect it to compile. We call `mutate_and_share`, which mutably borrows
124+
`foo` *temporarily*, but then returns *only* a shared reference. Therefore we
125+
would expect `foo.share()` to succeed as `foo` shouldn't be mutably borrowed.
126+
127+
However when we try to compile it:
128+
129+
```text
130+
<anon>:11:5: 11:8 error: cannot borrow `foo` as immutable because it is also borrowed as mutable
131+
<anon>:11 foo.share();
132+
^~~
133+
<anon>:10:16: 10:19 note: previous borrow of `foo` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foo` until the borrow ends
134+
<anon>:10 let loan = foo.mutate_and_share();
135+
^~~
136+
<anon>:12:2: 12:2 note: previous borrow ends here
137+
<anon>:8 fn main() {
138+
<anon>:9 let mut foo = Foo;
139+
<anon>:10 let loan = foo.mutate_and_share();
140+
<anon>:11 foo.share();
141+
<anon>:12 }
142+
^
105143
```
106144

107-
One would expect the output of foo to be an immutable lifetime. However we have
108-
derived it from the input, which is a mutable lifetime. So although we have a
109-
shared reference, it will have the much more limited aliasing rules of a mutable
110-
reference. As a consequence, there is no expressive benefit in a method that
111-
mutates returning a shared reference.
145+
What happened? Well, the lifetime of `loan` is derived from a *mutable* borrow.
146+
This makes the type system believe that `foo` is mutably borrowed as long as
147+
`loan` exists, even though it's a shared reference. To my knowledge, this is not
148+
a bug.
112149

113150

114151

@@ -413,7 +450,7 @@ respectively.
413450

414451

415452

416-
## PhantomData and PhantomFn
453+
## PhantomData
417454

418455
This is all well and good for the types the standard library provides, but
419456
how is variance determined for type that *you* define? A struct is, informally
@@ -475,6 +512,8 @@ pub struct Iter<'a, T: 'a> {
475512
```
476513

477514

515+
516+
478517
## Splitting Lifetimes
479518

480519
The mutual exclusion property of mutable references can be very limiting when
@@ -544,7 +583,76 @@ fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
544583
```
545584

546585
This is pretty plainly dangerous. We use transmute to duplicate the slice with an
547-
*unbounded* lifetime, so that it
586+
*unbounded* lifetime, so that it can be treated as disjoint from the other until
587+
we unify them when we return.
588+
589+
However more subtle is how iterators that yield mutable references work.
590+
The iterator trait is defined as follows:
591+
592+
```rust
593+
trait Iterator {
594+
type Item;
595+
596+
fn next(&mut self) -> Option<Self::Item>;
597+
}
598+
```
599+
600+
Given this definition, Self::Item has *no* connection to `self`. This means
601+
that we can call `next` several times in a row, and hold onto all the results
602+
*concurrently*. This is perfectly fine for by-value iterators, which have exactly
603+
these semantics. It's also actually fine for shared references, as it's perfectly
604+
fine to grab a huge pile of shared references to the same thing (although the
605+
iterator needs to be a separate object from the thing being shared). But mutable
606+
references make this a mess. At first glance, they might seem completely
607+
incompatible with this API, as it would produce multiple mutable references to
608+
the same object!
609+
610+
However it actually *does* work, exactly because iterators are one-shot objects.
611+
Everything an IterMut yields will be yielded *at most* once, so we don't *actually*
612+
ever yield multiple mutable references to the same piece of data.
613+
614+
In general all mutable iterators require *some* unsafe code *somewhere*, though.
615+
Whether it's raw pointers, or safely composing on top of *another* IterMut.
616+
617+
For instance, VecDeque's IterMut:
618+
619+
```rust
620+
pub struct IterMut<'a, T:'a> {
621+
// The whole backing array. Some of these indices are initialized!
622+
ring: &'a mut [T],
623+
tail: usize,
624+
head: usize,
625+
}
626+
627+
impl<'a, T> Iterator for IterMut<'a, T> {
628+
type Item = &'a mut T;
629+
630+
fn next(&mut self) -> Option<&'a mut T> {
631+
if self.tail == self.head {
632+
return None;
633+
}
634+
let tail = self.tail;
635+
self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len());
636+
637+
unsafe {
638+
// might as well do unchecked indexing since wrap_index has us
639+
// in-bounds, and many of the "middle" indices are uninitialized
640+
// anyway.
641+
let elem = self.ring.get_unchecked_mut(tail);
642+
643+
// round-trip through a raw pointer to unbound the lifetime from
644+
// ourselves
645+
Some(&mut *(elem as *mut _))
646+
}
647+
}
648+
}
649+
```
548650

651+
A very subtle but interesting detail in this design is that it *relies on
652+
privacy to be sound*. Borrowck works on some very simple rules. One of those rules
653+
is that if we have a live &mut Foo and Foo contains an &mut Bar, then that &mut
654+
Bar is *also* live. Since IterMut is always live when `next` can be called, if
655+
`ring` were public then we could mutate `ring` while outstanding mutable borrows
656+
to it exist!
549657

550658

0 commit comments

Comments
 (0)