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 to label scopes with lifetimes, and desugar the examples from the start of
this chapter. 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 around scopes and lifetimes, because writing everything out explicitly is
*extremely noisy*. All Rust code relies on aggressive inference and elision of *extremely noisy*. All Rust code relies on aggressive inference and elision of
"obvious" things. "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: How about the other example:

@ -1,5 +1,12 @@
% References % 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: There are two kinds of reference:
* Shared reference: `&` * Shared reference: `&`
@ -7,53 +14,63 @@ There are two kinds of reference:
Which obey the following rules: 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 # Paths
If all Rust had were values, then every value would be uniquely owned by a If all Rust had were values (no pointers), then every value would be uniquely
variable or composite structure. From this we naturally derive a *tree* of owned by a variable or composite structure. From this we naturally derive a
ownership. The stack itself is the root of the tree, with every variable as its *tree* of ownership. The stack itself is the root of the tree, with every
direct children. Each variable's direct children would be their fields (if any), variable as its direct children. Each variable's direct children would be their
and so on. fields (if any), and so on.
From this view, every value in Rust has a unique *path* in the tree of 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 ownership. Of particular interest are *ancestors* and *descendants*: if `x` owns
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
`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 that this is an inclusive relationship: `x` is a descendant and ancestor of
itself. 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 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 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 residing at the bottom of the stack (though we must be careful with mutable
globals). Data on the heap poses a different problem. 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, 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. then we could just treat such a pointer as a struct that owns the value on the
Box, Vec, String, and HashMap, are examples of types which uniquely own data on heap. Box, Vec, String, and HashMap, are examples of types which uniquely
the heap. own data on the heap.
Unfortunately, data on the heap is not *always* uniquely owned. Rc for instance 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 introduces a notion of *shared* ownership. Shared ownership of a value means
unique path. A value with no unique path limits what we can do with it. In there is no unique path to it. A value with no unique path limits what we can do
general, only shared references can be created to these values. However 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 mechanisms which ensure mutual exclusion may establish One True Owner
temporarily, establishing a unique path to that value (and therefore all its temporarily, establishing a unique path to that value (and therefore all
children). 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*, 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. in contrast to the *inherited mutability* that everything in Rust normally uses.
Cell, RefCell, Mutex, and RWLock are all examples of interior mutability types. Cell, RefCell, Mutex, and RWLock are all examples of interior mutability types.
These types provide exclusive access through runtime restrictions. However it is These types provide exclusive access through runtime restrictions.
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 An interesting case of this effect is Rc itself: if an Rc has refcount 1,
internals. 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 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 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 # Liveness
Note: Liveness is not the same thing as a *lifetime*, which will be explained 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. print it! Therefore `&mut` should be invariant.
This is the general theme of variance vs invariance: if variance would allow you 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. invariant.
However it *is* sound for `&'a mut T` to be variant over `'a`. The key difference However it *is* sound for `&'a mut T` to be variant over `'a`. The key difference

Loading…
Cancel
Save