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
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user