Skip to content

Commit ae861f0

Browse files
committed
Copyedit sections 5 and 6 of the borrowed pointer tutorial
1 parent bbd16a0 commit ae861f0

File tree

1 file changed

+77
-71
lines changed

1 file changed

+77
-71
lines changed

doc/tutorial-borrowed-ptr.md

Lines changed: 77 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -178,25 +178,27 @@ as well as from the managed box, and then compute the distance between them.
178178

179179
# Borrowing managed boxes and rooting
180180

181-
We’ve seen a few examples so far where heap boxes (both managed and
182-
unique) are borrowed. Up till this point, we’ve glossed over issues of
181+
We’ve seen a few examples so far of borrowing heap boxes, both managed
182+
and unique. Up till this point, we’ve glossed over issues of
183183
safety. As stated in the introduction, at runtime a borrowed pointer
184-
is simply a pointer, nothing more. Therefore, if we wish to avoid the
185-
issues that C has with dangling pointers (and we do!), a compile-time
186-
safety check is required.
187-
188-
The basis for the check is the notion of _lifetimes_. A lifetime is
189-
basically a static approximation of the period in which the pointer is
190-
valid: it always corresponds to some expression or block within the
191-
program. Within that expression, the pointer can be used freely, but
192-
if the pointer somehow leaks outside of that expression, the compiler
193-
will report an error. We’ll be discussing lifetimes more in the
194-
examples to come, and a more thorough introduction is also available.
195-
196-
When a borrowed pointer is created, the compiler must ensure that it
197-
will remain valid for its entire lifetime. Sometimes this is
198-
relatively easy, such as when taking the address of a local variable
199-
or a field that is stored on the stack:
184+
is simply a pointer, nothing more. Therefore, avoiding C's problems
185+
with dangling pointers requires a compile-time safety check.
186+
187+
The basis for the check is the notion of _lifetimes_. A lifetime is a
188+
static approximation of the span of execution during which the pointer
189+
is valid: it always corresponds to some expression or block within the
190+
program. Code inside that expression can use the pointer without
191+
restrictions. But if the pointer escapes from that expression (for
192+
example, if the expression contains an assignment expression that
193+
assigns the pointer to a mutable field of a data structure with a
194+
broader scope than the pointer itself), the compiler reports an
195+
error. We'll be discussing lifetimes more in the examples to come, and
196+
a more thorough introduction is also available.
197+
198+
When the `&` operator creates a borrowed pointer, the compiler must
199+
ensure that the pointer remains valid for its entire
200+
lifetime. Sometimes this is relatively easy, such as when taking the
201+
address of a local variable or a field that is stored on the stack:
200202

201203
~~~
202204
struct X { f: int }
@@ -207,12 +209,12 @@ fn example1() {
207209
} // -+
208210
~~~
209211

210-
Here, the lifetime of the borrowed pointer is simply L, the remainder
211-
of the function body. No extra work is required to ensure that `x.f`
212-
will not be freed. This is true even if `x` is mutated.
212+
Here, the lifetime of the borrowed pointer `y` is simply L, the
213+
remainder of the function body. The compiler need not do any other
214+
work to prove that code will not free `x.f`. This is true even if the
215+
code mutates `x`.
213216

214-
The situation gets more complex when borrowing data that resides in
215-
heap boxes:
217+
The situation gets more complex when borrowing data inside heap boxes:
216218

217219
~~~
218220
# struct X { f: int }
@@ -223,20 +225,25 @@ fn example2() {
223225
} // -+
224226
~~~
225227

226-
In this example, the value `x` is in fact a heap box, and `y` is
227-
therefore a pointer into that heap box. Again the lifetime of `y` will
228-
be L, the remainder of the function body. But there is a crucial
229-
difference: suppose `x` were reassigned during the lifetime L? If
230-
we’re not careful, that could mean that the managed box would become
231-
unrooted and therefore be subject to garbage collection
228+
In this example, the value `x` is a heap box, and `y` is therefore a
229+
pointer into that heap box. Again the lifetime of `y` is L, the
230+
remainder of the function body. But there is a crucial difference:
231+
suppose `x` were to be reassigned during the lifetime L? If the
232+
compiler isn't careful, the managed box could become *unrooted*, and
233+
would therefore be subject to garbage collection. A heap box that is
234+
unrooted is one such that no pointer values in the heap point to
235+
it. It would violate memory safety for the box that was originally
236+
assigned to `x` to be garbage-collected, since a non-heap
237+
pointer---`y`---still points into it.
232238

233-
> ***Note:***In our current implementation, the garbage collector is
234-
> implemented using reference counting and cycle detection.
239+
> ***Note:*** Our current implementation implements the garbage collector
240+
> using reference counting and cycle detection.
235241
236-
For this reason, whenever the interior of a managed box stored in a
237-
mutable location is borrowed, the compiler will insert a temporary
238-
that ensures that the managed box remains live for the entire
239-
lifetime. So, the above example would be compiled as:
242+
For this reason, whenever an `&` expression borrows the interior of a
243+
managed box stored in a mutable location, the compiler inserts a
244+
temporary that ensures that the managed box remains live for the
245+
entire lifetime. So, the above example would be compiled as if it were
246+
written
240247

241248
~~~
242249
# struct X { f: int }
@@ -255,9 +262,9 @@ process is called *rooting*.
255262

256263
The previous example demonstrated *rooting*, the process by which the
257264
compiler ensures that managed boxes remain live for the duration of a
258-
borrow. Unfortunately, rooting does not work if the data being
259-
borrowed is a unique box, as it is not possible to have two references
260-
to a unique box.
265+
borrow. Unfortunately, rooting does not work for borrows of unique
266+
boxes, because it is not possible to have two references to a unique
267+
box.
261268

262269
For unique boxes, therefore, the compiler will only allow a borrow *if
263270
the compiler can guarantee that the unique box will not be reassigned
@@ -280,14 +287,14 @@ fn example3() -> int {
280287
~~~
281288

282289
Here, as before, the interior of the variable `x` is being borrowed
283-
and `x` is declared as mutable. However, the compiler can clearly see
284-
that `x` is not assigned anywhere in the lifetime L of the variable
290+
and `x` is declared as mutable. However, the compiler can prove that
291+
`x` is not assigned anywhere in the lifetime L of the variable
285292
`y`. Therefore, it accepts the function, even though `x` is mutable
286293
and in fact is mutated later in the function.
287294

288-
It may not be clear why we are so concerned about the variable which
289-
was borrowed being mutated. The reason is that unique boxes are freed
290-
_as soon as their owning reference is changed or goes out of
295+
It may not be clear why we are so concerned about mutating a borrowed
296+
variable. The reason is that the runtime system frees any unique box
297+
_as soon as its owning reference changes or goes out of
291298
scope_. Therefore, a program like this is illegal (and would be
292299
rejected by the compiler):
293300

@@ -332,11 +339,11 @@ Once the reassignment occurs, the memory will look like this:
332339
Here you can see that the variable `y` still points at the old box,
333340
which has been freed.
334341

335-
In fact, the compiler can apply this same kind of reasoning can be
336-
applied to any memory which is _(uniquely) owned by the stack
337-
frame_. So we could modify the previous example to introduce
338-
additional unique pointers and structs, and the compiler will still be
339-
able to detect possible mutations:
342+
In fact, the compiler can apply the same kind of reasoning to any
343+
memory that is _(uniquely) owned by the stack frame_. So we could
344+
modify the previous example to introduce additional unique pointers
345+
and structs, and the compiler will still be able to detect possible
346+
mutations:
340347

341348
~~~ {.xfail-test}
342349
fn example3() -> int {
@@ -353,11 +360,11 @@ fn example3() -> int {
353360

354361
In this case, two errors are reported, one when the variable `x` is
355362
modified and another when `x.f` is modified. Either modification would
356-
cause the pointer `y` to be invalidated.
363+
invalidate the pointer `y`.
357364

358-
Things get tricker when the unique box is not uniquely owned by the
359-
stack frame (or when the compiler doesn’t know who the owner
360-
is). Consider a program like this:
365+
Things get trickier when the unique box is not uniquely owned by the
366+
stack frame, or when there is no way for the compiler to determine the
367+
box's owner. Consider a program like this:
361368

362369
~~~
363370
struct R { g: int }
@@ -381,18 +388,18 @@ Here the heap looks something like:
381388
+------+
382389
~~~
383390

384-
In this case, the owning reference to the value being borrowed is in
385-
fact `x.f`. Moreover, `x.f` is both mutable and aliasable. Aliasable
386-
means that it is possible that there are other pointers to that same
387-
managed box, so even if the compiler were to prevent `x.f` from being
388-
mutated, the field might still be changed through some alias of
389-
`x`. Therefore, to be safe, the compiler only accepts pure actions
390-
during the lifetime of `y`. We’ll have a final example on purity but
391-
inn unique fields, as in the following example:
391+
In this case, the owning reference to the value being borrowed is
392+
`x.f`. Moreover, `x.f` is both mutable and *aliasable*. Aliasable
393+
means that there may be other pointers to that same managed box, so
394+
even if the compiler were to prove an absence of mutations to `x.f`,
395+
code could mutate `x.f` indirectly by changing an alias of
396+
`x`. Therefore, to be safe, the compiler only accepts *pure* actions
397+
during the lifetime of `y`. We define what "pure" means in the section
398+
on [purity](#purity).
392399

393400
Besides ensuring purity, the only way to borrow the interior of a
394-
unique found in aliasable memory is to ensure that it is stored within
395-
unique fields, as in the following example:
401+
unique found in aliasable memory is to ensure that the borrowed field
402+
itself is also unique, as in the following example:
396403

397404
~~~
398405
struct R { g: int }
@@ -409,7 +416,7 @@ the compiler to know that, even if aliases to `x` exist, the field `f`
409416
cannot be changed and hence the unique box `g` will remain valid.
410417

411418
If you do have a unique box in a mutable field, and you wish to borrow
412-
it, one option is to use the swap operator to bring that unique box
419+
it, one option is to use the swap operator to move that unique box
413420
onto your stack:
414421

415422
~~~
@@ -430,14 +437,13 @@ fn example5c(x: @S) -> int {
430437

431438
Of course, this has the side effect of modifying your managed box for
432439
the duration of the borrow, so it only works when you know that you
433-
won’t be accessing that same box for the duration of the loan. Note
434-
also that sometimes it is necessary to introduce additional blocks to
435-
constrain the scope of the loan. In this example, the borrowed
436-
pointer `y` would still be in scope when you moved the value `v` back
437-
into `x.f`, and hence moving `v` would be considered illegal. You
438-
cannot move values if they are outstanding loans which are still
439-
valid. By introducing the block, the scope of `y` is restricted and so
440-
the move is legal.
440+
won't be accessing that same box for the duration of the loan. Also,
441+
it is sometimes necessary to introduce additional blocks to constrain
442+
the scope of the loan. In this example, the borrowed pointer `y`
443+
would still be in scope when you moved the value `v` back into `x.f`,
444+
and hence moving `v` would be considered illegal. You cannot move
445+
values if they are the targets of valid outstanding loans. Introducing
446+
the block restricts the scope of `y`, making the move legal.
441447

442448
# Borrowing and enums
443449

0 commit comments

Comments
 (0)