|
1 |
| -% Ownership |
| 1 | +% Ownership and Lifetimes |
2 | 2 |
|
3 | 3 | Ownership is the breakout feature of Rust. It allows Rust to be completely
|
4 | 4 | memory-safe and efficient, while avoiding garbage collection. Before getting
|
@@ -58,6 +58,13 @@ a Cell is to copy the bits in or out.
|
58 | 58 | manages this through *runtime* checks. It is effectively a thread-unsafe
|
59 | 59 | read-write lock.
|
60 | 60 |
|
| 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 | + |
61 | 68 |
|
62 | 69 |
|
63 | 70 |
|
@@ -96,19 +103,49 @@ more than a local lint against incorrect usage of a value.
|
96 | 103 |
|
97 | 104 | ## Weird Lifetimes
|
98 | 105 |
|
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: |
102 | 107 |
|
103 | 108 | ```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 | + ^ |
105 | 143 | ```
|
106 | 144 |
|
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. |
112 | 149 |
|
113 | 150 |
|
114 | 151 |
|
@@ -413,7 +450,7 @@ respectively.
|
413 | 450 |
|
414 | 451 |
|
415 | 452 |
|
416 |
| -## PhantomData and PhantomFn |
| 453 | +## PhantomData |
417 | 454 |
|
418 | 455 | This is all well and good for the types the standard library provides, but
|
419 | 456 | how is variance determined for type that *you* define? A struct is, informally
|
@@ -475,6 +512,8 @@ pub struct Iter<'a, T: 'a> {
|
475 | 512 | ```
|
476 | 513 |
|
477 | 514 |
|
| 515 | + |
| 516 | + |
478 | 517 | ## Splitting Lifetimes
|
479 | 518 |
|
480 | 519 | 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]) {
|
544 | 583 | ```
|
545 | 584 |
|
546 | 585 | 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 | +``` |
548 | 650 |
|
| 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! |
549 | 657 |
|
550 | 658 |
|
0 commit comments