Skip to content

Commit 6c8e6aa

Browse files
committed
tutorial: rewrite the sections on boxes/moves
1 parent c151d37 commit 6c8e6aa

File tree

1 file changed

+70
-58
lines changed

1 file changed

+70
-58
lines changed

doc/tutorial.md

+70-58
Original file line numberDiff line numberDiff line change
@@ -923,22 +923,60 @@ custom destructors.
923923

924924
# Boxes
925925

926-
Many modern languages represent values as pointers to heap memory by
927-
default. In contrast, Rust, like C and C++, represents such types directly.
928-
Another way to say this is that aggregate data in Rust are *unboxed*. This
929-
means that if you `let x = Point { x: 1f, y: 1f };`, you are creating a struct
930-
on the stack. If you then copy it into a data structure, you copy the entire
931-
struct, not just a pointer.
926+
A value in Rust is stored directly inside the owner. If a `struct` contains
927+
four `int` fields, it will be four times as large as a single `int`. The
928+
following `struct` type is invalid, as it would have an infinite size:
932929

933-
For small structs like `Point`, this is usually more efficient than allocating
934-
memory and indirecting through a pointer. But for big structs, or mutable
935-
state, it can be useful to have a single copy on the stack or on the heap, and
936-
refer to that through a pointer.
930+
~~~~ {.xfail-test}
931+
struct List {
932+
next: Option<List>,
933+
data: int
934+
}
935+
~~~~
936+
937+
> ***Note:*** The `Option` type is an enum representing an *optional* value.
938+
> It's comparable to a nullable pointer in many other languages, but stores the
939+
> contained value unboxed.
940+
941+
An *owned box* (`~`) uses a heap allocation to provide the invariant of always
942+
being the size of a pointer, regardless of the contained type. This can be
943+
leveraged to create a valid recursive `struct` type with a finite size:
944+
945+
~~~~
946+
struct List {
947+
next: Option<~List>,
948+
data: int
949+
}
950+
~~~~
951+
952+
Since an owned box has a single owner, they are limited to representing
953+
tree-like data structures.
954+
955+
The most common use case for owned boxes is creating recursive data structures
956+
like a binary search tree. Rust's trait-based generics system (covered later in
957+
the tutorial) is usually used for static dispatch, but also provides dynamic
958+
dispatch via boxing. Values of different types may have different sizes, but a
959+
box is able to *erase* the difference via the layer of indirection they
960+
provide.
937961

938-
## Owned boxes
962+
In uncommon cases, the indirection can provide a performance gain or memory
963+
reduction by making values smaller. However, unboxed values should almost
964+
always be preferred.
939965

940-
An owned box (`~`) is a uniquely owned allocation on the heap. It inherits the
941-
mutability and lifetime of the owner as it would if there was no box:
966+
Note that returning large unboxed values via boxes is unnecessary. A large
967+
value is returned via a hidden output parameter, and the decision on where to
968+
place the return value should be left to the caller:
969+
970+
~~~~
971+
fn foo() -> (int, int, int, int, int, int) {
972+
(5, 5, 5, 5, 5, 5)
973+
}
974+
975+
let x = ~foo(); // allocates, and writes the integers directly to it
976+
~~~~
977+
978+
Beyond the properties granted by the size, an owned box behaves as a regular
979+
value by inheriting the mutability and lifetime of the owner:
942980

943981
~~~~
944982
let x = 5; // immutable
@@ -950,35 +988,33 @@ let mut y = ~5; // mutable
950988
*y += 2; // the * operator is needed to access the contained value
951989
~~~~
952990

953-
The purpose of an owned box is to add a layer of indirection in order to create
954-
recursive data structures or cheaply pass around an object larger than a
955-
pointer. Since an owned box has a unique owner, it can only be used to
956-
represent a tree data structure.
991+
As covered earlier, an owned box has a destructor to clean up the allocated
992+
memory. This makes it more restricted than an unboxed type with no destructor
993+
by introducing *move semantics*.
957994

958-
The following struct won't compile, because the lack of indirection would mean
959-
it has an infinite size:
995+
# Move semantics
960996

961-
~~~~ {.xfail-test}
962-
struct Foo {
963-
child: Option<Foo>
964-
}
965-
~~~~
997+
Rust uses a shallow copy for parameter passing, assignment and returning from
998+
functions. This is considered a move of ownership for types with destructors.
999+
After a value has been moved, it can no longer be used from the source location
1000+
and will not be destroyed when the source goes out of scope.
9661001

967-
> ***Note:*** The `Option` type is an enum that represents an *optional* value.
968-
> It's comparable to a nullable pointer in many other languages, but stores the
969-
> contained value unboxed.
1002+
~~~~
1003+
let x = ~5;
1004+
let y = x.clone(); // y is a newly allocated box
1005+
let z = x; // no new memory allocated, x can no longer be used
1006+
~~~~
9701007

971-
Adding indirection with an owned pointer allocates the child outside of the
972-
struct on the heap, which makes it a finite size and won't result in a
973-
compile-time error:
1008+
The mutability of a value may be changed by moving it to a new owner:
9741009

9751010
~~~~
976-
struct Foo {
977-
child: Option<~Foo>
978-
}
1011+
let r = ~13;
1012+
let mut s = r; // box becomes mutable
1013+
*s += 1;
1014+
let t = s; // box becomes immutable
9791015
~~~~
9801016

981-
## Managed boxes
1017+
# Managed boxes
9821018

9831019
A managed box (`@`) is a heap allocation with the lifetime managed by a
9841020
task-local garbage collector. It will be destroyed at some point after there
@@ -1023,30 +1059,6 @@ d = b; // box type is the same, okay
10231059
c = b; // error
10241060
~~~~
10251061

1026-
# Move semantics
1027-
1028-
Rust uses a shallow copy for parameter passing, assignment and returning values
1029-
from functions. A shallow copy is considered a move of ownership if the
1030-
ownership tree of the copied value includes an owned box or a type with a
1031-
custom destructor. After a value has been moved, it can no longer be used from
1032-
the source location and will not be destroyed there.
1033-
1034-
~~~~
1035-
let x = ~5;
1036-
let y = x.clone(); // y is a newly allocated box
1037-
let z = x; // no new memory allocated, x can no longer be used
1038-
~~~~
1039-
1040-
Since in owned boxes mutability is a property of the owner, not the
1041-
box, mutable boxes may become immutable when they are moved, and vice-versa.
1042-
1043-
~~~~
1044-
let r = ~13;
1045-
let mut s = r; // box becomes mutable
1046-
*s += 1;
1047-
let t = s; // box becomes immutable
1048-
~~~~
1049-
10501062
# Borrowed pointers
10511063

10521064
Rust's borrowed pointers are a general purpose reference type. In contrast with

0 commit comments

Comments
 (0)