|
|
|
@ -1,5 +1,12 @@
|
|
|
|
|
% References
|
|
|
|
|
|
|
|
|
|
This section gives a high-level view of the memory model that *all* Rust
|
|
|
|
|
programs must satisfy to be correct. Safe code is statically verified
|
|
|
|
|
to obey this model by the borrow checker. Unsafe code may go above
|
|
|
|
|
and beyond the borrow checker while still satisfying this model. The borrow
|
|
|
|
|
checker may also be extended to allow more programs to compile, as long as
|
|
|
|
|
this more fundamental model is satisfied.
|
|
|
|
|
|
|
|
|
|
There are two kinds of reference:
|
|
|
|
|
|
|
|
|
|
* Shared reference: `&`
|
|
|
|
@ -7,53 +14,63 @@ There are two kinds of reference:
|
|
|
|
|
|
|
|
|
|
Which obey the following rules:
|
|
|
|
|
|
|
|
|
|
* A reference cannot outlive its referent A mutable reference cannot be aliased
|
|
|
|
|
* A reference cannot outlive its referent
|
|
|
|
|
* A mutable reference cannot be aliased
|
|
|
|
|
|
|
|
|
|
To define aliasing, we must define the notion of *paths* and *liveness*.
|
|
|
|
|
That's it. That's the whole model. Of course, we should probably define
|
|
|
|
|
what *aliased* means. To define aliasing, we must define the notion of
|
|
|
|
|
*paths* and *liveness*.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Paths
|
|
|
|
|
|
|
|
|
|
If all Rust had were values, then every value would be uniquely owned by a
|
|
|
|
|
variable or composite structure. From this we naturally derive a *tree* of
|
|
|
|
|
ownership. The stack itself is the root of the tree, with every variable as its
|
|
|
|
|
direct children. Each variable's direct children would be their fields (if any),
|
|
|
|
|
and so on.
|
|
|
|
|
If all Rust had were values (no pointers), then every value would be uniquely
|
|
|
|
|
owned by a variable or composite structure. From this we naturally derive a
|
|
|
|
|
*tree* of ownership. The stack itself is the root of the tree, with every
|
|
|
|
|
variable as its direct children. Each variable's direct children would be their
|
|
|
|
|
fields (if any), and so on.
|
|
|
|
|
|
|
|
|
|
From this view, every value in Rust has a unique *path* in the tree of
|
|
|
|
|
ownership. References to a value can subsequently be interpreted as a path in
|
|
|
|
|
this tree. Of particular interest are *ancestors* and *descendants*: if `x` owns
|
|
|
|
|
`y`, then `x` is an *ancestor* of `y`, and `y` is a *descendant* of `x`. Note
|
|
|
|
|
ownership. Of particular interest are *ancestors* and *descendants*: if `x` owns
|
|
|
|
|
`y`, then `x` is an ancestor of `y`, and `y` is a descendant of `x`. Note
|
|
|
|
|
that this is an inclusive relationship: `x` is a descendant and ancestor of
|
|
|
|
|
itself.
|
|
|
|
|
|
|
|
|
|
We can then define references as simply *names* for paths. When you create a
|
|
|
|
|
reference, you're declaring that an ownership path exists to this address
|
|
|
|
|
of memory.
|
|
|
|
|
|
|
|
|
|
Tragically, plenty of data doesn't reside on the stack, and we must also
|
|
|
|
|
accommodate this. Globals and thread-locals are simple enough to model as
|
|
|
|
|
residing at the bottom of the stack (though we must be careful with mutable
|
|
|
|
|
globals). Data on the heap poses a different problem.
|
|
|
|
|
|
|
|
|
|
If all Rust had on the heap was data uniquely owned by a pointer on the stack,
|
|
|
|
|
then we can just treat that pointer as a struct that owns the value on the heap.
|
|
|
|
|
Box, Vec, String, and HashMap, are examples of types which uniquely own data on
|
|
|
|
|
the heap.
|
|
|
|
|
then we could just treat such a pointer as a struct that owns the value on the
|
|
|
|
|
heap. Box, Vec, String, and HashMap, are examples of types which uniquely
|
|
|
|
|
own data on the heap.
|
|
|
|
|
|
|
|
|
|
Unfortunately, data on the heap is not *always* uniquely owned. Rc for instance
|
|
|
|
|
introduces a notion of *shared* ownership. Shared ownership means there is no
|
|
|
|
|
unique path. A value with no unique path limits what we can do with it. In
|
|
|
|
|
general, only shared references can be created to these values. However
|
|
|
|
|
introduces a notion of *shared* ownership. Shared ownership of a value means
|
|
|
|
|
there is no unique path to it. A value with no unique path limits what we can do
|
|
|
|
|
with it.
|
|
|
|
|
|
|
|
|
|
In general, only shared references can be created to non-unique paths. However
|
|
|
|
|
mechanisms which ensure mutual exclusion may establish One True Owner
|
|
|
|
|
temporarily, establishing a unique path to that value (and therefore all its
|
|
|
|
|
children).
|
|
|
|
|
temporarily, establishing a unique path to that value (and therefore all
|
|
|
|
|
its children). If this is done, the value may be mutated. In particular, a
|
|
|
|
|
mutable reference can be taken.
|
|
|
|
|
|
|
|
|
|
The most common way to establish such a path is through *interior mutability*,
|
|
|
|
|
in contrast to the *inherited mutability* that everything in Rust normally uses.
|
|
|
|
|
Cell, RefCell, Mutex, and RWLock are all examples of interior mutability types.
|
|
|
|
|
These types provide exclusive access through runtime restrictions. However it is
|
|
|
|
|
also possible to establish unique ownership without interior mutability. For
|
|
|
|
|
instance, if an Rc has refcount 1, then it is safe to mutate or move its
|
|
|
|
|
internals.
|
|
|
|
|
These types provide exclusive access through runtime restrictions.
|
|
|
|
|
|
|
|
|
|
An interesting case of this effect is Rc itself: if an Rc has refcount 1,
|
|
|
|
|
then it is safe to mutate or even move its internals. Note however that the
|
|
|
|
|
refcount itself uses interior mutability.
|
|
|
|
|
|
|
|
|
|
In order to correctly communicate to the type system that a variable or field of
|
|
|
|
|
a struct can have interior mutability, it must be wrapped in an UnsafeCell. This
|
|
|
|
@ -62,6 +79,7 @@ that value. You still must yourself ensure that mutual exclusion is upheld.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Liveness
|
|
|
|
|
|
|
|
|
|
Note: Liveness is not the same thing as a *lifetime*, which will be explained
|
|
|
|
|