@@ -447,11 +447,11 @@ the block restricts the scope of `y`, making the move legal.
447
447
448
448
# Borrowing and enums
449
449
450
- The previous example showed that borrowing unique boxes found in
451
- aliasable, mutable memory is not permitted, so as to prevent pointers
452
- into freed memory. There is one other case where the compiler must be
453
- very careful to ensure that pointers remain valid: pointers into the
454
- interior of an enum.
450
+ The previous example showed that the type system forbids any borrowing
451
+ of unique boxes found in aliasable, mutable memory. This restriction
452
+ prevents pointers from pointing into freed memory. There is one other
453
+ case where the compiler must be very careful to ensure that pointers
454
+ remain valid: pointers into the interior of an ` enum ` .
455
455
456
456
As an example, let’s look at the following ` shape ` type that can
457
457
represent both rectangles and circles:
@@ -465,9 +465,9 @@ enum Shape {
465
465
}
466
466
~~~
467
467
468
- Now I might write a function to compute the area of a shape. This
469
- function takes a borrowed pointer to a shape to avoid the need of
470
- copying them .
468
+ Now we might write a function to compute the area of a shape. This
469
+ function takes a borrowed pointer to a shape, to avoid the need for
470
+ copying.
471
471
472
472
~~~
473
473
# struct Point {x: float, y: float}; // as before
@@ -485,21 +485,21 @@ fn compute_area(shape: &Shape) -> float {
485
485
}
486
486
~~~
487
487
488
- The first case matches against circles. Here the radius is extracted
489
- from the shape variant and used to compute the area of the circle
490
- (Like any up-to-date engineer, we use the [ tau circle constant ] [ tau ]
491
- and not that dreadfully outdated notion of pi).
488
+ The first case matches against circles. Here, the pattern extracts the
489
+ radius from the shape variant and the action uses it to compute the
490
+ area of the circle. (Like any up-to-date engineer, we use the [ tau
491
+ circle constant ] [ tau ] and not that dreadfully outdated notion of pi).
492
492
493
493
[ tau ] : http://www.math.utah.edu/~palais/pi.html
494
494
495
495
The second match is more interesting. Here we match against a
496
- rectangle and extract its size: but rather than copy the ` size ` struct,
497
- we use a by-reference binding to create a pointer to it. In other
498
- words, a pattern binding like ` ref size ` in fact creates a pointer of
499
- type ` &size ` into the _ interior of the enum_ .
496
+ rectangle and extract its size: but rather than copy the ` size `
497
+ struct, we use a by-reference binding to create a pointer to it. In
498
+ other words, a pattern binding like ` ref size ` binds the name ` size `
499
+ to a pointer of type ` &size ` into the _ interior of the enum_ .
500
500
501
- To make this more clear, let’ s look at a diagram of how things are
502
- laid out in memory in the case where ` shape ` points at a rectangle:
501
+ To make this more clear, let' s look at a diagram of memory layout in
502
+ the case where ` shape ` points at a rectangle:
503
503
504
504
~~~ {.notrust}
505
505
Stack Memory
@@ -523,8 +523,8 @@ the shape.
523
523
Perhaps you can see where the danger lies: if the shape were somehow
524
524
to be reassigned, perhaps to a circle, then although the memory used
525
525
to store that shape value would still be valid, _ it would have a
526
- different type_ ! This is shown in the following diagram, depicting what
527
- the state of memory would be if shape were overwritten with a circle:
526
+ different type_ ! The following diagram shows what memory would look
527
+ like if code overwrote ` shape ` with a circle:
528
528
529
529
~~~ {.notrust}
530
530
Stack Memory
@@ -538,20 +538,23 @@ Stack Memory
538
538
+---------------+
539
539
~~~
540
540
541
- As you can see, the ` size ` pointer would not be pointing at a ` float ` and
542
- not a struct. This is not good.
541
+ As you can see, the ` size ` pointer would be pointing at a ` float `
542
+ instead of a struct. This is not good: dereferencing the second field
543
+ of a ` float ` as if it were a struct with two fields would be a memory
544
+ safety violation.
543
545
544
546
So, in fact, for every ` ref ` binding, the compiler will impose the
545
547
same rules as the ones we saw for borrowing the interior of a unique
546
- box: it must be able to guarantee that the enum will not be
547
- overwritten for the duration of the borrow. In fact, the example I
548
- gave earlier would be considered safe. This is because the shape
549
- pointer has type ` &Shape ` , which means “borrowed pointer to immutable
550
- memory containing a shape”. If however the type of that pointer were
551
- ` &const Shape ` or ` &mut Shape ` , then the ref binding would not be
552
- permitted. Just as with unique boxes, the compiler will permit ref
553
- bindings into data owned by the stack frame even if it is mutable, but
554
- otherwise it requires that the data reside in immutable memory.
548
+ box: it must be able to guarantee that the ` enum ` will not be
549
+ overwritten for the duration of the borrow. In fact, the compiler
550
+ would accept the example we gave earlier. The example is safe because
551
+ the shape pointer has type ` &Shape ` , which means "borrowed pointer to
552
+ immutable memory containing a ` shape ` ". If, however, the type of that
553
+ pointer were ` &const Shape ` or ` &mut Shape ` , then the ref binding
554
+ would be ill-typed. Just as with unique boxes, the compiler will
555
+ permit ` ref ` bindings into data owned by the stack frame even if the
556
+ data are mutable, but otherwise it requires that the data reside in
557
+ immutable memory.
555
558
556
559
> *** Note:*** Right now, pattern bindings not explicitly annotated
557
560
> with ` ref ` or ` copy ` use a special mode of "implicit by reference".
@@ -560,11 +563,11 @@ otherwise it requires that the data reside in immutable memory.
560
563
561
564
# Returning borrowed pointers
562
565
563
- So far, all of the examples we’ ve looked at use borrowed pointers in a
564
- “downward” direction. That is, the borrowed pointer is created and
565
- then used during the method or code block which created it . It is also
566
- possible to return borrowed pointers to the caller, but as we'll see
567
- this requires some explicit annotation.
566
+ So far, all of the examples we' ve looked at use borrowed pointers in a
567
+ “downward” direction. That is, a method or code block creates a
568
+ borrowed pointer, then uses it within the same scope . It is also
569
+ possible to return borrowed pointers as the result of a function, but
570
+ as we'll see, doing so requires some explicit annotation.
568
571
569
572
For example, we could write a subroutine like this:
570
573
@@ -573,23 +576,25 @@ struct Point {x: float, y: float}
573
576
fn get_x(p: &r/Point) -> &r/float { &p.x }
574
577
~~~
575
578
576
- Here, the function ` get_x() ` returns a pointer into the structure it was
577
- given. The type of the parameter (` &r/Point ` ) and return type (` &r/float ` ) both
578
- make use of a new syntactic form that we have not seen so far. Here the identifier ` r `
579
- serves as an explicit name for the lifetime of the pointer. So in effect
580
- this function is declaring that it takes in a pointer with lifetime ` r ` and returns
581
- a pointer with that same lifetime.
579
+ Here, the function ` get_x() ` returns a pointer into the structure it
580
+ was given. The type of the parameter (` &r/Point ` ) and return type
581
+ (` &r/float ` ) both use a new syntactic form that we have not seen so
582
+ far. Here the identifier ` r ` names the lifetime of the pointer
583
+ explicitly. So in effect, this function declares that it takes a
584
+ pointer with lifetime ` r ` and returns a pointer with that same
585
+ lifetime.
582
586
583
587
In general, it is only possible to return borrowed pointers if they
584
- are derived from a borrowed pointer which was given as input to the
585
- procedure. In that case, they will always have the same lifetime as
586
- one of the parameters; named lifetimes are used to indicate which
587
- parameter that is.
588
+ are derived from a parameter to the procedure. In that case, the
589
+ pointer result will always have the same lifetime as one of the
590
+ parameters; named lifetimes indicate which parameter that
591
+ is.
588
592
589
- In the examples before, function parameter types did not include a
590
- lifetime name. In this case, the compiler simply creates a new,
591
- anonymous name, meaning that the parameter is assumed to have a
592
- distinct lifetime from all other parameters.
593
+ In the previous examples, function parameter types did not include a
594
+ lifetime name. In those examples, the compiler simply creates a fresh
595
+ name for the lifetime automatically: that is, the lifetime name is
596
+ guaranteed to refer to a distinct lifetime from the lifetimes of all
597
+ other parameters.
593
598
594
599
Named lifetimes that appear in function signatures are conceptually
595
600
the same as the other lifetimes we've seen before, but they are a bit
@@ -599,13 +604,13 @@ lifetime `r` is actually a kind of *lifetime parameter*: it is defined
599
604
by the caller to ` get_x() ` , just as the value for the parameter ` p ` is
600
605
defined by that caller.
601
606
602
- In any case, whatever the lifetime ` r ` is, the pointer produced by
603
- ` &p.x ` always has the same lifetime as ` p ` itself, as a pointer to a
607
+ In any case, whatever the lifetime of ` r ` is, the pointer produced by
608
+ ` &p.x ` always has the same lifetime as ` p ` itself: a pointer to a
604
609
field of a struct is valid as long as the struct is valid. Therefore,
605
- the compiler is satisfied with the function ` get_x() ` .
610
+ the compiler accepts the function ` get_x() ` .
606
611
607
- To drill in this point, let’s look at a variation on the example, this
608
- time one which does not compile:
612
+ To emphasize this point, let’s look at a variation on the example, this
613
+ time one that does not compile:
609
614
610
615
~~~ {.xfail-test}
611
616
struct Point {x: float, y: float}
@@ -617,22 +622,21 @@ fn get_x_sh(p: @Point) -> &float {
617
622
Here, the function ` get_x_sh() ` takes a managed box as input and
618
623
returns a borrowed pointer. As before, the lifetime of the borrowed
619
624
pointer that will be returned is a parameter (specified by the
620
- caller). That means that effectively ` get_x_sh() ` is promising to
621
- return a borrowed pointer that is valid for as long as the caller
622
- would like: this is subtly different from the first example, which
623
- promised to return a pointer that was valid for as long as the pointer
624
- it was given.
625
+ caller). That means that ` get_x_sh() ` promises to return a borrowed
626
+ pointer that is valid for as long as the caller would like: this is
627
+ subtly different from the first example, which promised to return a
628
+ pointer that was valid for as long as its pointer argument was valid.
625
629
626
630
Within ` get_x_sh() ` , we see the expression ` &p.x ` which takes the
627
- address of a field of a managed box. This implies that the compiler
628
- must guarantee that, so long as the resulting pointer is valid, the
629
- managed box will not be reclaimed by the garbage collector. But recall
630
- that ` get_x_sh() ` also promised to return a pointer that was valid for
631
- as long as the caller wanted it to be. Clearly, ` get_x_sh() ` is not in
632
- a position to make both of these guarantees; in fact, it cannot
633
- guarantee that the pointer will remain valid at all once it returns,
634
- as the parameter ` p ` may or may not be live in the caller. Therefore,
635
- the compiler will report an error here.
631
+ address of a field of a managed box. The presence of this expression
632
+ implies that the compiler must guarantee that, so long as the
633
+ resulting pointer is valid, the managed box will not be reclaimed by
634
+ the garbage collector. But recall that ` get_x_sh() ` also promised to
635
+ return a pointer that was valid for as long as the caller wanted it to
636
+ be. Clearly, ` get_x_sh() ` is not in a position to make both of these
637
+ guarantees; in fact, it cannot guarantee that the pointer will remain
638
+ valid at all once it returns, as the parameter ` p ` may or may not be
639
+ live in the caller. Therefore, the compiler will report an error here.
636
640
637
641
In general, if you borrow a managed (or unique) box to create a
638
642
borrowed pointer, the pointer will only be valid within the function
@@ -643,9 +647,9 @@ points at a static constant).
643
647
644
648
# Named lifetimes
645
649
646
- Let's look at named lifetimes in more detail. In effect, the use of
647
- named lifetimes allows you to group parameters by lifetime. For
648
- example, consider this function:
650
+ Let's look at named lifetimes in more detail. Named lifetimes allow
651
+ for grouping of parameters by lifetime. For example, consider this
652
+ function:
649
653
650
654
~~~
651
655
# struct Point {x: float, y: float}; // as before
@@ -718,10 +722,10 @@ fn select<T>(shape: &tmp/Shape, threshold: float,
718
722
}
719
723
~~~
720
724
721
- Here you can see the lifetime of shape is now being called ` tmp ` . The
722
- parameters ` a ` , ` b ` , and the return value are all given the lifetime
723
- ` r ` . However, since the lifetime ` tmp ` is not returned, it would be shorter
724
- to just omit the named lifetime for ` shape ` altogether:
725
+ Here you can see that ` shape ` 's lifetime is now named ` tmp ` . The
726
+ parameters ` a ` , ` b ` , and the return value all have the lifetime ` r ` .
727
+ However, since the lifetime ` tmp ` is not returned, it would be more
728
+ concise to just omit the named lifetime for ` shape ` altogether:
725
729
726
730
~~~
727
731
# struct Point {x: float, y: float}; // as before
@@ -742,17 +746,22 @@ This is equivalent to the previous definition.
742
746
# Purity
743
747
744
748
As mentioned before, the Rust compiler offers a kind of escape hatch
745
- that permits borrowing of any data, but only if the actions that occur
749
+ that permits borrowing of any data, as long as the actions that occur
746
750
during the lifetime of the borrow are pure. Pure actions are those
747
- which only modify data owned by the current stack frame. The compiler
751
+ that only modify data owned by the current stack frame. The compiler
748
752
can therefore permit arbitrary pointers into the heap, secure in the
749
753
knowledge that no pure action will ever cause them to become
750
754
invalidated (the compiler must still track data on the stack which is
751
- borrowed and enforce those rules normally, of course).
752
-
753
- Let’s revisit a previous example and show how purity can affect the
754
- compiler’s result. Here is ` example5a() ` , which borrows the interior of
755
- a unique box found in an aliasable, mutable location, only now we’ve
755
+ borrowed and enforce those rules normally, of course). A pure function
756
+ in Rust is referentially transparent: it returns the same results
757
+ given the same (observably equivalent) inputs. That is because while
758
+ pure functions are allowed to modify data, they may only modify
759
+ * stack-local* data, which cannot be observed outside the scope of the
760
+ function itself. (Using an ` unsafe ` block invalidates this guarantee.)
761
+
762
+ Let’s revisit a previous example and show how purity can affect
763
+ typechecking. Here is ` example5a() ` , which borrows the interior of a
764
+ unique box found in an aliasable, mutable location, only now we’ve
756
765
replaced the ` ... ` with some specific code:
757
766
758
767
~~~
@@ -764,8 +773,8 @@ fn example5a(x: @S ...) -> int {
764
773
}
765
774
~~~
766
775
767
- The new code simply returns an incremented version of ` y ` . This clearly
768
- doesn’t do mutate anything in the heap, so the compiler is satisfied.
776
+ The new code simply returns an incremented version of ` y ` . This code
777
+ clearly doesn't mutate the heap, so the compiler is satisfied.
769
778
770
779
But suppose we wanted to pull the increment code into a helper, like
771
780
this:
@@ -787,8 +796,8 @@ fn example5a(x: @S ...) -> int {
787
796
~~~
788
797
789
798
But now the compiler will report an error again. The reason is that it
790
- only considers one function at a time (like most type checkers ), and
791
- so it does not know that ` add_one() ` only takes pure actions . We can
799
+ only considers one function at a time (like most typecheckers ), and
800
+ so it does not know that ` add_one() ` consists of pure code . We can
792
801
help the compiler by labeling ` add_one() ` as pure:
793
802
794
803
~~~
@@ -799,7 +808,7 @@ With this change, the modified version of `example5a()` will again compile.
799
808
800
809
# Conclusion
801
810
802
- So there you have it. A (relatively) brief tour of borrowed pointer
803
- system. For more details, I refer to the (yet to be written) reference
811
+ So there you have it: a (relatively) brief tour of the borrowed pointer
812
+ system. For more details, we refer to the (yet to be written) reference
804
813
document on borrowed pointers, which will explain the full notation
805
814
and give more examples.
0 commit comments