@@ -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 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 ` .
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.
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 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.
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 .
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 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).
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).
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 `
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_ .
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_ .
500
500
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:
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:
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_ ! The following diagram shows what memory would look
527
- like if code overwrote ` shape ` with a circle:
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:
528
528
529
529
~~~ {.notrust}
530
530
Stack Memory
@@ -538,23 +538,20 @@ Stack Memory
538
538
+---------------+
539
539
~~~
540
540
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.
541
+ As you can see, the ` size ` pointer would not be pointing at a ` float ` and
542
+ not a struct. This is not good.
545
543
546
544
So, in fact, for every ` ref ` binding, the compiler will impose the
547
545
same rules as the ones we saw for borrowing the interior of a unique
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.
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.
558
555
559
556
> *** Note:*** Right now, pattern bindings not explicitly annotated
560
557
> with ` ref ` or ` copy ` use a special mode of "implicit by reference".
@@ -563,11 +560,11 @@ immutable memory.
563
560
564
561
# Returning borrowed pointers
565
562
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.
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.
571
568
572
569
For example, we could write a subroutine like this:
573
570
@@ -576,25 +573,23 @@ struct Point {x: float, y: float}
576
573
fn get_x(p: &r/Point) -> &r/float { &p.x }
577
574
~~~
578
575
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.
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.
586
582
587
583
In general, it is only possible to return borrowed pointers if they
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.
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.
592
588
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.
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.
598
593
599
594
Named lifetimes that appear in function signatures are conceptually
600
595
the same as the other lifetimes we've seen before, but they are a bit
@@ -604,13 +599,13 @@ lifetime `r` is actually a kind of *lifetime parameter*: it is defined
604
599
by the caller to ` get_x() ` , just as the value for the parameter ` p ` is
605
600
defined by that caller.
606
601
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
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
609
604
field of a struct is valid as long as the struct is valid. Therefore,
610
- the compiler accepts the function ` get_x() ` .
605
+ the compiler is satisfied with the function ` get_x() ` .
611
606
612
- To emphasize this point, let’s look at a variation on the example, this
613
- time one that does not compile:
607
+ To drill in this point, let’s look at a variation on the example, this
608
+ time one which does not compile:
614
609
615
610
~~~ {.xfail-test}
616
611
struct Point {x: float, y: float}
@@ -622,21 +617,22 @@ fn get_x_sh(p: @Point) -> &float {
622
617
Here, the function ` get_x_sh() ` takes a managed box as input and
623
618
returns a borrowed pointer. As before, the lifetime of the borrowed
624
619
pointer that will be returned is a parameter (specified by the
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.
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.
629
625
630
626
Within ` get_x_sh() ` , we see the expression ` &p.x ` which takes the
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.
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.
640
636
641
637
In general, if you borrow a managed (or unique) box to create a
642
638
borrowed pointer, the pointer will only be valid within the function
@@ -647,9 +643,9 @@ points at a static constant).
647
643
648
644
# Named lifetimes
649
645
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:
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:
653
649
654
650
~~~
655
651
# struct Point {x: float, y: float}; // as before
@@ -722,10 +718,10 @@ fn select<T>(shape: &tmp/Shape, threshold: float,
722
718
}
723
719
~~~
724
720
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:
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:
729
725
730
726
~~~
731
727
# struct Point {x: float, y: float}; // as before
@@ -746,22 +742,17 @@ This is equivalent to the previous definition.
746
742
# Purity
747
743
748
744
As mentioned before, the Rust compiler offers a kind of escape hatch
749
- that permits borrowing of any data, as long as the actions that occur
745
+ that permits borrowing of any data, but only if the actions that occur
750
746
during the lifetime of the borrow are pure. Pure actions are those
751
- that only modify data owned by the current stack frame. The compiler
747
+ which only modify data owned by the current stack frame. The compiler
752
748
can therefore permit arbitrary pointers into the heap, secure in the
753
749
knowledge that no pure action will ever cause them to become
754
750
invalidated (the compiler must still track data on the stack which is
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
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
765
756
replaced the ` ... ` with some specific code:
766
757
767
758
~~~
@@ -773,8 +764,8 @@ fn example5a(x: @S ...) -> int {
773
764
}
774
765
~~~
775
766
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.
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.
778
769
779
770
But suppose we wanted to pull the increment code into a helper, like
780
771
this:
@@ -796,8 +787,8 @@ fn example5a(x: @S ...) -> int {
796
787
~~~
797
788
798
789
But now the compiler will report an error again. The reason is that it
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
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
801
792
help the compiler by labeling ` add_one() ` as pure:
802
793
803
794
~~~
@@ -808,7 +799,7 @@ With this change, the modified version of `example5a()` will again compile.
808
799
809
800
# Conclusion
810
801
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
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
813
804
document on borrowed pointers, which will explain the full notation
814
805
and give more examples.
0 commit comments