@@ -1010,22 +1010,22 @@ generics](#generics).
1010
1010
1011
1011
# The Rust memory model
1012
1012
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
1014
1014
in Rust's memory model. We've seen some of Rust's pointer sigils (` @ ` ,
1015
1015
` ~ ` , and ` & ` ) float by in a few examples, and we aren't going to get
1016
1016
much further without explaining them. Rust has a very particular
1017
1017
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.
1020
1021
1021
1022
Rust has three competing goals that inform its view of memory:
1022
1023
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
1029
1029
optional and, if it is not desired, memory safety must not be compromised.
1030
1030
Less performance-critical, high-level code should be able to employ a single,
1031
1031
garbage-collection-based, heap allocation strategy.
@@ -1034,7 +1034,7 @@ Rust has three competing goals that inform its view of memory:
1034
1034
1035
1035
## How performance considerations influence the memory model
1036
1036
1037
- Most languages that offer strong memory safety guarantees rely upon a
1037
+ Most languages that offer strong memory safety guarantees rely on a
1038
1038
garbage-collected heap to manage all of the objects. This approach is
1039
1039
straightforward both in concept and in implementation, but has
1040
1040
significant costs. Languages that follow this path tend to
@@ -1044,18 +1044,20 @@ boxes_: memory allocated on the heap whose lifetime is managed
1044
1044
by the garbage collector.
1045
1045
1046
1046
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
1048
1048
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
1051
1051
objects after they are destroyed.
1052
1052
1053
+ [ borrow ] : tutorial-borrowed-ptr.html
1054
+
1053
1055
## How concurrency considerations influence the memory model
1054
1056
1055
1057
Memory safety in a concurrent environment involves avoiding race
1056
1058
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.
1059
1061
1060
1062
Rust starts from the position that memory cannot be shared between
1061
1063
tasks. Experience in other languages has proven that isolating each
@@ -1064,28 +1066,30 @@ easy for programmers to reason about. Heap isolation has the
1064
1066
additional benefit that garbage collection must only be done
1065
1067
per-heap. Rust never "stops the world" to reclaim memory.
1066
1068
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.
1077
1079
1078
1080
# Boxes and pointers
1079
1081
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.
1086
1090
1087
1091
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
1089
1093
those with mutable fields, it can be useful to have a single copy on
1090
1094
the stack or on the heap, and refer to that through a pointer.
1091
1095
@@ -1100,16 +1104,15 @@ All pointer types can be dereferenced with the `*` unary operator.
1100
1104
> *** Note*** : You may also hear managed boxes referred to as 'shared
1101
1105
> boxes' or 'shared pointers', and owned boxes as 'unique boxes/pointers'.
1102
1106
> Borrowed pointers are sometimes called 'region pointers'. The preferred
1103
- > terminology is as presented here.
1107
+ > terminology is what we present here.
1104
1108
1105
1109
## Managed boxes
1106
1110
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.
1113
1116
1114
1117
~~~~
1115
1118
let x: @int = @10; // New box
@@ -1119,8 +1122,8 @@ let y = x; // Copy of a pointer to the same box
1119
1122
// then the allocation will be freed.
1120
1123
~~~~
1121
1124
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 .
1124
1127
1125
1128
~~~
1126
1129
// A linked list node
@@ -1148,19 +1151,19 @@ node3.prev = SomeNode(node2);
1148
1151
1149
1152
Managed boxes never cross task boundaries.
1150
1153
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.
1154
1157
1155
1158
## Owned boxes
1156
1159
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.
1162
1165
1163
- Because owned boxes are uniquely owned, copying them involves allocating
1166
+ Because owned boxes are uniquely owned, copying them requires allocating
1164
1167
a new owned box and duplicating the contents. Copying owned boxes
1165
1168
is expensive so the compiler will complain if you do so without writing
1166
1169
the word ` copy ` .
@@ -1180,11 +1183,11 @@ let z = *x + *y;
1180
1183
assert z == 20;
1181
1184
~~~~
1182
1185
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 ).
1188
1191
1189
1192
~~~~ {.xfail-test}
1190
1193
let x = ~10;
@@ -1198,16 +1201,16 @@ to other tasks. The sending task will give up ownership of the box,
1198
1201
and won't be able to access it afterwards. The receiving task will
1199
1202
become the sole owner of the box.
1200
1203
1201
- > *** Note:*** this discussion of copying vs moving does not account
1204
+ > *** Note:*** This discussion of copying vs. moving does not account
1202
1205
> 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
1204
1207
> favor of explicit moves.
1205
1208
1206
1209
## Borrowed pointers
1207
1210
1208
1211
Rust borrowed pointers are a general purpose reference/pointer type,
1209
1212
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
1211
1214
pointer is the owner of the pointed-to memory, borrowed pointers never
1212
1215
imply ownership. Pointers may be borrowed from any type, in which case
1213
1216
the pointer is guaranteed not to outlive the value it points to.
@@ -1220,9 +1223,9 @@ struct Point {
1220
1223
}
1221
1224
~~~~
1222
1225
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 :
1226
1229
1227
1230
~~~
1228
1231
# struct Point { x: float, y: float }
@@ -1306,7 +1309,8 @@ let sum = *managed + *owned + *borrowed;
1306
1309
~~~
1307
1310
1308
1311
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.
1310
1314
1311
1315
~~~
1312
1316
let managed = @mut 10;
@@ -1321,8 +1325,8 @@ let borrowed = &mut value;
1321
1325
~~~
1322
1326
1323
1327
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 .
1326
1330
1327
1331
~~~
1328
1332
# struct Point { x: float, y: float }
@@ -1334,9 +1338,9 @@ let rect = &Rectangle(*start, *end);
1334
1338
let area = (* rect).area();
1335
1339
~~~
1336
1340
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.
1340
1344
1341
1345
~~~
1342
1346
# struct Point { x: float, y: float }
@@ -1348,16 +1352,17 @@ let rect = &Rectangle(*start, *end);
1348
1352
let area = rect.area();
1349
1353
~~~
1350
1354
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
1353
1358
1354
1359
~~~
1355
1360
# struct Point { x: float, y: float }
1356
1361
let point = &@~ Point { x: 10f, y: 20f };
1357
1362
io::println(fmt!("%f", point.x));
1358
1363
~~~
1359
1364
1360
- The indexing operator (`[]`) is also auto-dereferencing .
1365
+ The indexing operator (`[]`) also auto-dereferences .
1361
1366
1362
1367
# Vectors and strings
1363
1368
0 commit comments