fixups for aturon

pull/10/head
Alexis Beingessner 9 years ago committed by Manish Goregaokar
parent 8685cdba24
commit b1529f107e

@ -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

Loading…
Cancel
Save