fixups for aturon
This commit is contained in:
@@ -19,7 +19,7 @@ our toes with lifetimes, we're going to pretend that we're actually allowed
|
||||
to label scopes with lifetimes, and desugar the examples from the start of
|
||||
this chapter.
|
||||
|
||||
Our examples made use of *aggressive* sugar -- high fructose corn syrup even --
|
||||
Originally, our examples made use of *aggressive* sugar -- high fructose corn syrup even --
|
||||
around scopes and lifetimes, because writing everything out explicitly is
|
||||
*extremely noisy*. All Rust code relies on aggressive inference and elision of
|
||||
"obvious" things.
|
||||
@@ -166,7 +166,7 @@ our implementation *just a bit*.)
|
||||
|
||||
|
||||
|
||||
# Example 2: aliasing a mutable reference
|
||||
# Example: aliasing a mutable reference
|
||||
|
||||
How about the other example:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -100,7 +100,7 @@ subsequently be dropped, and `forever_str` would point to freed memory when we
|
||||
print it! Therefore `&mut` should be invariant.
|
||||
|
||||
This is the general theme of variance vs invariance: if variance would allow you
|
||||
to *store* a short-lived value over a longer-lived slot, then you must be
|
||||
to store a short-lived value into a longer-lived slot, then you must be
|
||||
invariant.
|
||||
|
||||
However it *is* sound for `&'a mut T` to be variant over `'a`. The key difference
|
||||
|
||||
Reference in New Issue
Block a user