1
1
# References
2
2
3
- This section gives a high-level view of the memory model that * all* Rust
4
- programs must satisfy to be correct. Safe code is statically verified
5
- to obey this model by the borrow checker. Unsafe code may go above
6
- and beyond the borrow checker while still satisfying this model. The borrow
7
- checker may also be extended to allow more programs to compile, as long as
8
- this more fundamental model is satisfied.
9
-
10
3
There are two kinds of reference:
11
4
12
5
* Shared reference: ` & `
@@ -17,161 +10,22 @@ Which obey the following rules:
17
10
* A reference cannot outlive its referent
18
11
* A mutable reference cannot be aliased
19
12
20
- That's it. That's the whole model. Of course, we should probably define
21
- what * aliased* means. To define aliasing, we must define the notion of
22
- * paths* and * liveness* .
23
-
24
-
25
- ** NOTE: The model that follows is generally agreed to be dubious and have
26
- issues. It's ok-ish as an intuitive model, but fails to capture the desired
27
- semantics. We leave this here to be able to use notions introduced here in later
28
- sections. This will be significantly changed in the future. TODO: do that.**
29
-
30
-
31
- # Paths
32
-
33
- If all Rust had were values (no pointers), then every value would be uniquely
34
- owned by a variable or composite structure. From this we naturally derive a
35
- * tree* of ownership. The stack itself is the root of the tree, with every
36
- variable as its direct children. Each variable's direct children would be their
37
- fields (if any), and so on.
38
-
39
- From this view, every value in Rust has a unique * path* in the tree of
40
- ownership. Of particular interest are * ancestors* and * descendants* : if ` x ` owns
41
- ` y ` , then ` x ` is an ancestor of ` y ` , and ` y ` is a descendant of ` x ` . Note
42
- that this is an inclusive relationship: ` x ` is a descendant and ancestor of
43
- itself.
44
-
45
- We can then define references as simply * names* for paths. When you create a
46
- reference, you're declaring that an ownership path exists to this address
47
- of memory.
48
-
49
- Tragically, plenty of data doesn't reside on the stack, and we must also
50
- accommodate this. Globals and thread-locals are simple enough to model as
51
- residing at the bottom of the stack (though we must be careful with mutable
52
- globals). Data on the heap poses a different problem.
53
-
54
- If all Rust had on the heap was data uniquely owned by a pointer on the stack,
55
- then we could just treat such a pointer as a struct that owns the value on the
56
- heap. Box, Vec, String, and HashMap, are examples of types which uniquely
57
- own data on the heap.
58
-
59
- Unfortunately, data on the heap is not * always* uniquely owned. Rc for instance
60
- introduces a notion of * shared* ownership. Shared ownership of a value means
61
- there is no unique path to it. A value with no unique path limits what we can do
62
- with it.
63
-
64
- In general, only shared references can be created to non-unique paths. However
65
- mechanisms which ensure mutual exclusion may establish One True Owner
66
- temporarily, establishing a unique path to that value (and therefore all
67
- its children). If this is done, the value may be mutated. In particular, a
68
- mutable reference can be taken.
69
-
70
- The most common way to establish such a path is through * interior mutability* ,
71
- in contrast to the * inherited mutability* that everything in Rust normally uses.
72
- Cell, RefCell, Mutex, and RWLock are all examples of interior mutability types.
73
- These types provide exclusive access through runtime restrictions.
74
-
75
- An interesting case of this effect is Rc itself: if an Rc has refcount 1,
76
- then it is safe to mutate or even move its internals. Note however that the
77
- refcount itself uses interior mutability.
78
-
79
- In order to correctly communicate to the type system that a variable or field of
80
- a struct can have interior mutability, it must be wrapped in an UnsafeCell. This
81
- does not in itself make it safe to perform interior mutability operations on
82
- that value. You still must yourself ensure that mutual exclusion is upheld.
83
-
84
-
85
-
13
+ That's it. That's the whole model references follow.
86
14
87
- # Liveness
15
+ Of course, we should probably define what * aliased * means.
88
16
89
- Note: Liveness is not the same thing as a * lifetime* , which will be explained
90
- in detail in the next section of this chapter.
17
+ ``` text
18
+ error[E0425]: cannot find value `aliased` in this scope
19
+ --> <rust.rs>:2:20
20
+ |
21
+ 2 | println!("{}", aliased);
22
+ | ^^^^^^^ not found in this scope
91
23
92
- Roughly, a reference is * live* at some point in a program if it can be
93
- dereferenced. Shared references are always live unless they are literally
94
- unreachable (for instance, they reside in freed or leaked memory). Mutable
95
- references can be reachable but * not* live through the process of * reborrowing* .
96
-
97
- A mutable reference can be reborrowed to either a shared or mutable reference to
98
- one of its descendants. A reborrowed reference will only be live again once all
99
- reborrows derived from it expire. For instance, a mutable reference can be
100
- reborrowed to point to a field of its referent:
101
-
102
- ``` rust
103
- let x = & mut (1 , 2 );
104
- {
105
- // reborrow x to a subfield
106
- let y = & mut x . 0 ;
107
- // y is now live, but x isn't
108
- * y = 3 ;
109
- }
110
- // y goes out of scope, so x is live again
111
- * x = (5 , 7 );
24
+ error: aborting due to previous error
112
25
```
113
26
114
- It is also possible to reborrow into * multiple* mutable references, as long as
115
- they are * disjoint* : no reference is an ancestor of another. Rust
116
- explicitly enables this to be done with disjoint struct fields, because
117
- disjointness can be statically proven:
118
-
119
- ``` rust
120
- let x = & mut (1 , 2 );
121
- {
122
- // reborrow x to two disjoint subfields
123
- let y = & mut x . 0 ;
124
- let z = & mut x . 1 ;
125
-
126
- // y and z are now live, but x isn't
127
- * y = 3 ;
128
- * z = 4 ;
129
- }
130
- // y and z go out of scope, so x is live again
131
- * x = (5 , 7 );
132
- ```
133
-
134
- However it's often the case that Rust isn't sufficiently smart to prove that
135
- multiple borrows are disjoint. * This does not mean it is fundamentally illegal
136
- to make such a borrow* , just that Rust isn't as smart as you want.
137
-
138
- To simplify things, we can model variables as a fake type of reference: * owned*
139
- references. Owned references have much the same semantics as mutable references:
140
- they can be re-borrowed in a mutable or shared manner, which makes them no
141
- longer live. Live owned references have the unique property that they can be
142
- moved out of (though mutable references * can* be swapped out of). This power is
143
- only given to * live* owned references because moving its referent would of
144
- course invalidate all outstanding references prematurely.
145
-
146
- As a local lint against inappropriate mutation, only variables that are marked
147
- as ` mut ` can be borrowed mutably.
148
-
149
- It is interesting to note that Box behaves exactly like an owned reference. It
150
- can be moved out of, and Rust understands it sufficiently to reason about its
151
- paths like a normal variable.
152
-
153
-
154
-
155
-
156
- # Aliasing
157
-
158
- With liveness and paths defined, we can now properly define * aliasing* :
159
-
160
- ** A mutable reference is aliased if there exists another live reference to one
161
- of its ancestors or descendants.**
162
-
163
- (If you prefer, you may also say the two live references alias * each other* .
164
- This has no semantic consequences, but is probably a more useful notion when
165
- verifying the soundness of a construct.)
166
-
167
- That's it. Super simple right? Except for the fact that it took us two pages to
168
- define all of the terms in that definition. You know: Super. Simple.
169
-
170
- Actually it's a bit more complicated than that. In addition to references, Rust
171
- has * raw pointers* : ` *const T ` and ` *mut T ` . Raw pointers have no inherent
172
- ownership or aliasing semantics. As a result, Rust makes absolutely no effort to
173
- track that they are used correctly, and they are wildly unsafe.
27
+ Unfortunately, Rust hasn't actually defined its aliasing model. 🙀
174
28
175
- ** It is an open question to what degree raw pointers have alias semantics.
176
- However it is important for these definitions to be sound that the existence of
177
- a raw pointer does not imply some kind of live path. **
29
+ While we wait for the Rust devs to specify the semantics of their language,
30
+ let's use the next section to discuss what aliasing is in general, and why it
31
+ matters.
0 commit comments