Skip to content

Commit 7582a48

Browse files
committed
Copyedit sections 7 and 8 of the tutorial
1 parent 1a8b00a commit 7582a48

File tree

1 file changed

+75
-70
lines changed

1 file changed

+75
-70
lines changed

doc/tutorial.md

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,22 +1010,22 @@ generics](#generics).
10101010
10111011
# The Rust memory model
10121012

1013-
At this junction let's take a detour to explain the concepts involved
1013+
At this junction, let's take a detour to explain the concepts involved
10141014
in Rust's memory model. We've seen some of Rust's pointer sigils (`@`,
10151015
`~`, and `&`) float by in a few examples, and we aren't going to get
10161016
much further without explaining them. Rust has a very particular
10171017
approach to memory management that plays a significant role in shaping
1018-
the "feel" of the language. Understanding the memory landscape will
1019-
illuminate several of Rust's unique features as we encounter them.
1018+
the subjective experience of programming in the
1019+
language. Understanding the memory landscape will illuminate several
1020+
of Rust's unique features as we encounter them.
10201021

10211022
Rust has three competing goals that inform its view of memory:
10221023

1023-
* Memory safety: Memory that is managed by and is accessible to the
1024-
Rust language must be guaranteed to be valid. Under normal
1025-
circumstances it must be impossible for Rust to trigger a
1026-
segmentation fault or leak memory.
1027-
* Performance: High-performance low-level code must be able to employ
1028-
a number of allocation strategies. Tracing garbage collection must be
1024+
* Memory safety: Memory that the Rust language can observe must be
1025+
guaranteed to be valid. Under normal circumstances, it must be
1026+
impossible for Rust to trigger a segmentation fault or leak memory.
1027+
* Performance: High-performance low-level code must be able to use
1028+
a number of different allocation strategies. Tracing garbage collection must be
10291029
optional and, if it is not desired, memory safety must not be compromised.
10301030
Less performance-critical, high-level code should be able to employ a single,
10311031
garbage-collection-based, heap allocation strategy.
@@ -1034,7 +1034,7 @@ Rust has three competing goals that inform its view of memory:
10341034

10351035
## How performance considerations influence the memory model
10361036

1037-
Most languages that offer strong memory safety guarantees rely upon a
1037+
Most languages that offer strong memory safety guarantees rely on a
10381038
garbage-collected heap to manage all of the objects. This approach is
10391039
straightforward both in concept and in implementation, but has
10401040
significant costs. Languages that follow this path tend to
@@ -1044,18 +1044,20 @@ boxes_: memory allocated on the heap whose lifetime is managed
10441044
by the garbage collector.
10451045

10461046
By comparison, languages like C++ offer very precise control over
1047-
where objects are allocated. In particular, it is common to put them
1047+
where objects are allocated. In particular, it is common to allocate them
10481048
directly on the stack, avoiding expensive heap allocation. In Rust
1049-
this is possible as well, and the compiler will use a clever _pointer
1050-
lifetime analysis_ to ensure that no variable can refer to stack
1049+
this is possible as well, and the compiler uses a [clever _pointer
1050+
lifetime analysis_][borrow] to ensure that no variable can refer to stack
10511051
objects after they are destroyed.
10521052

1053+
[borrow]: tutorial-borrowed-ptr.html
1054+
10531055
## How concurrency considerations influence the memory model
10541056

10551057
Memory safety in a concurrent environment involves avoiding race
10561058
conditions between two threads of execution accessing the same
1057-
memory. Even high-level languages often require programmers to
1058-
correctly employ locking to ensure that a program is free of races.
1059+
memory. Even high-level languages often require programmers to make
1060+
correct use of locking to ensure that a program is free of races.
10591061

10601062
Rust starts from the position that memory cannot be shared between
10611063
tasks. Experience in other languages has proven that isolating each
@@ -1064,28 +1066,30 @@ easy for programmers to reason about. Heap isolation has the
10641066
additional benefit that garbage collection must only be done
10651067
per-heap. Rust never "stops the world" to reclaim memory.
10661068

1067-
Complete isolation of heaps between tasks would, however, mean that any data
1068-
transferred between tasks must be copied. While this is a fine and
1069-
useful way to implement communication between tasks, it is also very
1070-
inefficient for large data structures. Because of this, Rust also
1071-
employs a global _exchange heap_. Objects allocated in the exchange
1072-
heap have _ownership semantics_, meaning that there is only a single
1073-
variable that refers to them. For this reason, they are referred to as
1074-
_owned boxes_. All tasks may allocate objects on the exchange heap,
1075-
then transfer ownership of those objects to other tasks, avoiding
1076-
expensive copies.
1069+
Complete isolation of heaps between tasks would, however, mean that
1070+
any data transferred between tasks must be copied. While this is a
1071+
fine and useful way to implement communication between tasks, it is
1072+
also very inefficient for large data structures. To reduce the amount
1073+
of copying, Rust also uses a global _exchange heap_. Objects allocated
1074+
in the exchange heap have _ownership semantics_, meaning that there is
1075+
only a single variable that refers to them. For this reason, they are
1076+
referred to as _owned boxes_. All tasks may allocate objects on the
1077+
exchange heap, then transfer ownership of those objects to other
1078+
tasks, avoiding expensive copies.
10771079

10781080
# Boxes and pointers
10791081

1080-
In contrast to a lot of modern languages, aggregate types like structs
1081-
and enums are _not_ represented as pointers to allocated memory in
1082-
Rust. They are, as in C and C++, represented directly. This means that
1083-
if you `let x = Point {x: 1f, y: 1f};`, you are creating a struct on the
1084-
stack. If you then copy it into a data structure, the whole struct is
1085-
copied, not just a pointer.
1082+
Many modern languages have a so-called "uniform representation" for
1083+
aggregate types like structs and enums, so as to represent these types
1084+
as pointers to heap memory by default. In contrast, Rust, like C and
1085+
C++, represents such types directly. Another way to say this is that
1086+
aggregate data in Rust are *unboxed*. This means that if you `let x =
1087+
Point {x: 1f, y: 1f};`, you are creating a struct on the stack. If you
1088+
then copy it into a data structure, you copy the entire struct, not
1089+
just a pointer.
10861090

10871091
For small structs like `Point`, this is usually more efficient than
1088-
allocating memory and going through a pointer. But for big structs, or
1092+
allocating memory and indirecting through a pointer. But for big structs, or
10891093
those with mutable fields, it can be useful to have a single copy on
10901094
the stack or on the heap, and refer to that through a pointer.
10911095

@@ -1100,16 +1104,15 @@ All pointer types can be dereferenced with the `*` unary operator.
11001104
> ***Note***: You may also hear managed boxes referred to as 'shared
11011105
> boxes' or 'shared pointers', and owned boxes as 'unique boxes/pointers'.
11021106
> Borrowed pointers are sometimes called 'region pointers'. The preferred
1103-
> terminology is as presented here.
1107+
> terminology is what we present here.
11041108
11051109
## Managed boxes
11061110

1107-
Managed boxes are pointers to heap-allocated, garbage collected memory.
1108-
Creating a managed box is done by simply applying the unary `@`
1109-
operator to an expression. The result of the expression will be boxed,
1110-
resulting in a box of the right type. Copying a shared box, as happens
1111-
during assignment, only copies a pointer, never the contents of the
1112-
box.
1111+
Managed boxes are pointers to heap-allocated, garbage collected
1112+
memory. Applying the unary `@` operator to an expression creates a
1113+
managed box. The resulting box contains the result of the
1114+
expression. Copying a shared box, as happens during assignment, only
1115+
copies a pointer, never the contents of the box.
11131116

11141117
~~~~
11151118
let x: @int = @10; // New box
@@ -1119,8 +1122,8 @@ let y = x; // Copy of a pointer to the same box
11191122
// then the allocation will be freed.
11201123
~~~~
11211124

1122-
Any type that contains managed boxes or other managed types is
1123-
considered _managed_.
1125+
A _managed_ type is either of the form `@T` for some type `T`, or any
1126+
type that contains managed boxes or other managed types.
11241127

11251128
~~~
11261129
// A linked list node
@@ -1148,19 +1151,19 @@ node3.prev = SomeNode(node2);
11481151

11491152
Managed boxes never cross task boundaries.
11501153

1151-
> ***Note:*** managed boxes are currently reclaimed through reference
1152-
> counting and cycle collection, but we will switch to a tracing
1153-
> garbage collector eventually.
1154+
> ***Note:*** Currently, the Rust compiler generates code to reclaim
1155+
> managed boxes through reference counting and a cycle collector, but
1156+
> we will switch to a tracing garbage collector eventually.
11541157
11551158
## Owned boxes
11561159

1157-
In contrast to managed boxes, owned boxes have a single owning memory
1158-
slot and thus two owned boxes may not refer to the same memory. All
1159-
owned boxes across all tasks are allocated on a single _exchange
1160-
heap_, where their uniquely owned nature allows them to be passed
1161-
between tasks efficiently.
1160+
In contrast with managed boxes, owned boxes have a single owning
1161+
memory slot and thus two owned boxes may not refer to the same
1162+
memory. All owned boxes across all tasks are allocated on a single
1163+
_exchange heap_, where their uniquely owned nature allows tasks to
1164+
exchange them efficiently.
11621165

1163-
Because owned boxes are uniquely owned, copying them involves allocating
1166+
Because owned boxes are uniquely owned, copying them requires allocating
11641167
a new owned box and duplicating the contents. Copying owned boxes
11651168
is expensive so the compiler will complain if you do so without writing
11661169
the word `copy`.
@@ -1180,11 +1183,11 @@ let z = *x + *y;
11801183
assert z == 20;
11811184
~~~~
11821185

1183-
This is where the 'move' operator comes in. It is similar to
1184-
`copy`, but it de-initializes its source. Thus, the owned box can move
1185-
from `x` to `y`, without violating the constraint that it only has a
1186-
single owner (if you used assignment instead of the move operator, the
1187-
box would, in principle, be copied).
1186+
This is where the 'move' operator comes in. It is similar to `copy`,
1187+
but it de-initializes its source. Thus, the owned box can move from
1188+
`x` to `y`, without violating the constraint that it only has a single
1189+
owner (using assignment instead of the move operator would, in
1190+
principle, copy the box).
11881191

11891192
~~~~ {.xfail-test}
11901193
let x = ~10;
@@ -1198,16 +1201,16 @@ to other tasks. The sending task will give up ownership of the box,
11981201
and won't be able to access it afterwards. The receiving task will
11991202
become the sole owner of the box.
12001203

1201-
> ***Note:*** this discussion of copying vs moving does not account
1204+
> ***Note:*** This discussion of copying vs. moving does not account
12021205
> for the "last use" rules that automatically promote copy operations
1203-
> to moves. Last use is expected to be removed from the language in
1206+
> to moves. We plan to remove last use from the language in
12041207
> favor of explicit moves.
12051208
12061209
## Borrowed pointers
12071210

12081211
Rust borrowed pointers are a general purpose reference/pointer type,
12091212
similar to the C++ reference type, but guaranteed to point to valid
1210-
memory. In contrast to owned pointers, where the holder of a unique
1213+
memory. In contrast with owned pointers, where the holder of a unique
12111214
pointer is the owner of the pointed-to memory, borrowed pointers never
12121215
imply ownership. Pointers may be borrowed from any type, in which case
12131216
the pointer is guaranteed not to outlive the value it points to.
@@ -1220,9 +1223,9 @@ struct Point {
12201223
}
12211224
~~~~
12221225
1223-
We can use this simple definition to allocate points in many ways. For
1224-
example, in this code, each of these three local variables contains a
1225-
point, but allocated in a different place:
1226+
We can use this simple definition to allocate points in many different
1227+
ways. For example, in this code, each of these three local variables
1228+
contains a point, but allocated in a different location:
12261229
12271230
~~~
12281231
# struct Point { x: float, y: float }
@@ -1306,7 +1309,8 @@ let sum = *managed + *owned + *borrowed;
13061309
~~~
13071310
13081311
Dereferenced mutable pointers may appear on the left hand side of
1309-
assignments, in which case the value they point to is modified.
1312+
assignments. Such an assignment modifies the value that the pointer
1313+
points to.
13101314
13111315
~~~
13121316
let managed = @mut 10;
@@ -1321,8 +1325,8 @@ let borrowed = &mut value;
13211325
~~~
13221326
13231327
Pointers have high operator precedence, but lower precedence than the
1324-
dot operator used for field and method access. This can lead to some
1325-
awkward code filled with parenthesis.
1328+
dot operator used for field and method access. This precedence order
1329+
can sometimes make code awkward and parenthesis-filled.
13261330
13271331
~~~
13281332
# struct Point { x: float, y: float }
@@ -1334,9 +1338,9 @@ let rect = &Rectangle(*start, *end);
13341338
let area = (*rect).area();
13351339
~~~
13361340
1337-
To combat this ugliness the dot operator performs _automatic pointer
1338-
dereferencing_ on the receiver (the value on the left hand side of the
1339-
dot), so in most cases dereferencing the receiver is not necessary.
1341+
To combat this ugliness the dot operator applies _automatic pointer
1342+
dereferencing_ to the receiver (the value on the left hand side of the
1343+
dot), so in most cases, explicitly dereferencing the receiver is not necessary.
13401344
13411345
~~~
13421346
# struct Point { x: float, y: float }
@@ -1348,16 +1352,17 @@ let rect = &Rectangle(*start, *end);
13481352
let area = rect.area();
13491353
~~~
13501354
1351-
Auto-dereferencing is performed through any number of pointers. If you
1352-
felt inclined you could write something silly like
1355+
You can write an expression that dereferences any number of pointers
1356+
automatically. For example, if you felt inclined, you could write
1357+
something silly like
13531358
13541359
~~~
13551360
# struct Point { x: float, y: float }
13561361
let point = &@~Point { x: 10f, y: 20f };
13571362
io::println(fmt!("%f", point.x));
13581363
~~~
13591364
1360-
The indexing operator (`[]`) is also auto-dereferencing.
1365+
The indexing operator (`[]`) also auto-dereferences.
13611366
13621367
# Vectors and strings
13631368

0 commit comments

Comments
 (0)