@@ -853,184 +853,170 @@ as in this example that unpacks the first value from a tuple and returns it.
853
853
fn first((value, _): (int, float)) -> int { value }
854
854
~~~
855
855
856
- # Destructors
856
+ # Boxes and pointers
857
+
858
+ Many modern languages have a so-called "uniform representation" for
859
+ aggregate types like structs and enums, so as to represent these types
860
+ as pointers to heap memory by default. In contrast, Rust, like C and
861
+ C++, represents such types directly. Another way to say this is that
862
+ aggregate data in Rust are * unboxed* . This means that if you `let x =
863
+ Point { x: 1f, y: 1f };`, you are creating a struct on the stack. If you
864
+ then copy it into a data structure, you copy the entire struct, not
865
+ just a pointer.
866
+
867
+ For small structs like ` Point ` , this is usually more efficient than
868
+ allocating memory and indirecting through a pointer. But for big structs, or
869
+ those with mutable fields, it can be useful to have a single copy on
870
+ the stack or on the heap, and refer to that through a pointer.
871
+
872
+ Whenever memory is allocated on the heap, the program needs a strategy to
873
+ dispose of the memory when no longer needed. Most languages, such as Java or
874
+ Python, use * garbage collection* for this, a strategy in which the program
875
+ periodically searches for allocations that are no longer reachable in order
876
+ to dispose of them. Other languages, such as C, use * manual memory
877
+ management* , which relies on the programmer to specify when memory should be
878
+ reclaimed.
879
+
880
+ Rust is in a different position. It differs from the garbage-collected
881
+ environments in that allows the programmer to choose the disposal
882
+ strategy on an object-by-object basis. Not only does this have benefits for
883
+ performance, but we will later see that this model has benefits for
884
+ concurrency as well, by making it possible for the Rust compiler to detect
885
+ data races at compile time. Rust also differs from the manually managed
886
+ languages in that it is * safe* —it uses a [ pointer lifetime
887
+ analysis] [ borrow ] to ensure that manual memory management cannot cause memory
888
+ errors at runtime.
857
889
858
- C-style resource management requires the programmer to match every allocation
859
- with a free, which means manually tracking the responsibility for cleaning up
860
- (the owner). Correctness is left to the programmer, and it's easy to get wrong.
861
-
862
- The following code demonstrates manual memory management, in order to contrast
863
- it with Rust's resource management. Rust enforces safety, so the ` unsafe `
864
- keyword is used to explicitly wrap the unsafe code. The keyword is a promise to
865
- the compiler that unsafety does not leak outside of the unsafe block, and is
866
- used to create safe concepts on top of low-level code.
890
+ [ borrow ] : tutorial-borrowed-ptr.html
867
891
868
- ~~~~
869
- use core::libc::funcs::c95::stdlib::{calloc, free};
870
- use core::libc::types::os::arch::c95::size_t;
892
+ The cornerstone of Rust's memory management is the concept of a * smart
893
+ pointer* —a pointer type that indicates the lifetime of the object it points
894
+ to. This solution is familiar to C++ programmers; Rust differs from C++,
895
+ however, in that a small set of smart pointers are built into the language.
896
+ The safe pointer types are ` @T ` , for * managed* boxes allocated on the * local
897
+ heap* , ` ~T ` , for * uniquely-owned* boxes allocated on the * exchange
898
+ heap* , and ` &T ` , for * borrowed* pointers, which may point to any memory, and
899
+ whose lifetimes are governed by the call stack.
871
900
872
- fn main() {
873
- unsafe {
874
- let a = calloc(1, int::bytes as size_t);
901
+ All pointer types can be dereferenced with the ` * ` unary operator.
875
902
876
- let d;
903
+ > *** Note*** : You may also hear managed boxes referred to as 'shared
904
+ > boxes' or 'shared pointers', and owned boxes as 'unique boxes/pointers'.
905
+ > Borrowed pointers are sometimes called 'region pointers'. The preferred
906
+ > terminology is what we present here.
877
907
878
- {
879
- let b = calloc(1, int::bytes as size_t);
880
-
881
- let c = calloc(1, int::bytes as size_t);
882
- d = c; // move ownership to d
908
+ ## Managed boxes
883
909
884
- free(b);
885
- }
910
+ Managed boxes are pointers to heap-allocated, garbage-collected
911
+ memory. Applying the unary ` @ ` operator to an expression creates a
912
+ managed box. The resulting box contains the result of the
913
+ expression. Copying a managed box, as happens during assignment, only
914
+ copies a pointer, never the contents of the box.
886
915
887
- free(d);
888
- free(a);
889
- }
890
- }
891
916
~~~~
917
+ let x: @int = @10; // New box
918
+ let y = x; // Copy of a pointer to the same box
892
919
893
- Rust uses destructors to handle the release of resources like memory
894
- allocations, files and sockets. An object will only be destroyed when there is
895
- no longer any way to access it, which prevents dynamic failures from an attempt
896
- to use a freed resource. When a task fails, the stack unwinds and the
897
- destructors of all objects owned by that task are called.
898
-
899
- The unsafe code from above can be contained behind a safe API that prevents
900
- memory leaks or use-after-free:
901
-
920
+ // x and y both refer to the same allocation. When both go out of scope
921
+ // then the allocation will be freed.
902
922
~~~~
903
- use core::libc::funcs::c95::stdlib::{calloc, free};
904
- use core::libc::types::common::c95::c_void;
905
- use core::libc::types::os::arch::c95::size_t;
906
923
907
- struct Blob { priv ptr: *c_void }
924
+ A _ managed_ type is either of the form ` @T ` for some type ` T ` , or any
925
+ type that contains managed boxes or other managed types.
908
926
909
- impl Blob {
910
- static fn new() -> Blob {
911
- unsafe { Blob{ptr: calloc(1, int::bytes as size_t)} }
912
- }
927
+ ~~~
928
+ // A linked list node
929
+ struct Node {
930
+ next: MaybeNode,
931
+ prev: MaybeNode,
932
+ payload: int
913
933
}
914
934
915
- impl Drop for Blob {
916
- fn finalize(&self) {
917
- unsafe { free(self.ptr); }
918
- }
935
+ enum MaybeNode {
936
+ SomeNode(@mut Node),
937
+ NoNode
919
938
}
920
939
921
- fn main() {
922
- let a = Blob::new();
923
-
924
- let d;
940
+ let node1 = @mut Node { next: NoNode, prev: NoNode, payload: 1 };
941
+ let node2 = @mut Node { next: NoNode, prev: NoNode, payload: 2 };
942
+ let node3 = @mut Node { next: NoNode, prev: NoNode, payload: 3 };
925
943
926
- {
927
- let b = Blob::new();
944
+ // Link the three list nodes together
945
+ node1.next = SomeNode(node2);
946
+ node2.prev = SomeNode(node1);
947
+ node2.next = SomeNode(node3);
948
+ node3.prev = SomeNode(node2);
949
+ ~~~
928
950
929
- let c = Blob::new();
930
- d = c; // move ownership to d
951
+ Managed boxes never cross task boundaries. This has several benefits for
952
+ performance:
931
953
932
- // b is destroyed here
933
- }
954
+ * The Rust garbage collector does not need to stop multiple threads in order
955
+ to collect garbage.
934
956
935
- // d is destroyed here
936
- // a is destroyed here
937
- }
938
- ~~~~
957
+ * You can separate your application into "real-time" tasks that do not use
958
+ the garbage collector and "non-real-time" tasks that do, and the real-time
959
+ tasks will not be interrupted by the non-real-time tasks.
939
960
940
- This pattern is common enough that Rust includes dynamically allocated memory
941
- as first-class types (` ~ ` and ` @ ` ). Non-memory resources like files are cleaned
942
- up with custom destructors.
961
+ C++ programmers will recognize ` @T ` as similar to ` std::shared_ptr<T> ` .
943
962
944
- ~~~~
945
- fn main() {
946
- let a = ~0;
963
+ > *** Note: *** Currently, the Rust compiler generates code to reclaim
964
+ > managed boxes through reference counting and a cycle collector, but
965
+ > we will switch to a tracing garbage collector eventually.
947
966
948
- let d;
967
+ ## Owned boxes
949
968
950
- {
951
- let b = ~0;
969
+ In contrast with managed boxes, owned boxes have a single owning
970
+ memory slot and thus two owned boxes may not refer to the same
971
+ memory. All owned boxes across all tasks are allocated on a single
972
+ _ exchange heap_ , where their uniquely-owned nature allows tasks to
973
+ exchange them efficiently.
952
974
953
- let c = ~0;
954
- d = c; // move ownership to d
975
+ Because owned boxes are uniquely owned, copying them requires allocating
976
+ a new owned box and duplicating the contents.
977
+ Instead, owned boxes are _ moved_ by default, transferring ownership,
978
+ and deinitializing the previously owning variable.
979
+ Any attempt to access a variable after the value has been moved out
980
+ will result in a compile error.
955
981
956
- // b is destroyed here
957
- }
958
-
959
- // d is destroyed here
960
- // a is destroyed here
961
- }
982
+ ~~~~
983
+ let x = ~10;
984
+ // Move x to y, deinitializing x
985
+ let y = x;
962
986
~~~~
963
987
964
- # Ownership
965
-
966
- Rust formalizes the concept of object ownership to delegate management of an
967
- object's lifetime to either a variable or a task-local garbage collector. An
968
- object's owner is responsible for managing the lifetime of the object by
969
- calling the destructor, and the owner determines whether the object is mutable.
970
-
971
- Ownership is recursive, so mutability is inherited recursively and a destructor
972
- destroys the contained tree of owned objects. Variables are top-level owners
973
- and destroy the contained object when they go out of scope. A box managed by
974
- the garbage collector starts a new ownership tree, and the destructor is called
975
- when it is collected.
976
-
977
- If an object doesn't contain garbage-collected boxes, it consists of a single
978
- ownership tree and is given the ` Owned ` trait which allows it to be sent
979
- between tasks.
980
-
981
- # Boxes
982
-
983
- Many modern languages represent values as as pointers to heap memory by
984
- default. In contrast, Rust, like C and C++, represents such types directly.
985
- Another way to say this is that aggregate data in Rust are * unboxed* . This
986
- means that if you ` let x = Point { x: 1f, y: 1f }; ` , you are creating a struct
987
- on the stack. If you then copy it into a data structure, you copy the entire
988
- struct, not just a pointer.
989
-
990
- For small structs like ` Point ` , this is usually more efficient than allocating
991
- memory and indirecting through a pointer. But for big structs, or mutable
992
- state, it can be useful to have a single copy on the stack or on the heap, and
993
- refer to that through a pointer.
994
-
995
- ## Owned boxes
996
-
997
- An owned box (` ~ ` ) is a uniquely owned allocation on the heap. An owned box
998
- inherits the mutability and lifetime of the owner as it would if there was no
999
- box. The purpose of an owned box is to add a layer of indirection in order to
1000
- create recursive data structures or cheaply pass around an object larger than a
1001
- pointer.
988
+ If you really want to copy an owned box you must say so explicitly.
1002
989
1003
- ## Managed boxes
990
+ ~~~~
991
+ let x = ~10;
992
+ let y = copy x;
1004
993
1005
- A managed box (` @ ` ) is a heap allocation with the lifetime managed by a
1006
- task-local garbage collector. It will be destroyed at some point after there
1007
- are no references left to the box, no later than the end of the task. Managed
1008
- boxes lack an owner, so they start a new ownership tree and don't inherit
1009
- mutability. They do own the contained object, and mutability is defined by the
1010
- type of the shared box (` @ ` or ` @mut ` ). An object containing a managed box is
1011
- not ` Owned ` , and can't be sent between tasks.
994
+ let z = *x + *y;
995
+ fail_unless!(z == 20);
996
+ ~~~~
1012
997
1013
- # Move semantics
998
+ When they do not contain any managed boxes, owned boxes can be sent
999
+ to other tasks. The sending task will give up ownership of the box
1000
+ and won't be able to access it afterwards. The receiving task will
1001
+ become the sole owner of the box. This prevents * data races* —errors
1002
+ that could otherwise result from multiple tasks working on the same
1003
+ data without synchronization.
1014
1004
1015
- Rust uses a shallow copy for parameter passing, assignment and returning values
1016
- from functions. A shallow copy is considered a move of ownership if the
1017
- ownership tree of the copied value includes an owned box or a type with a
1018
- custom destructor. After a value has been moved, it can no longer be used from
1019
- the source location and will not be destroyed there.
1005
+ When an owned pointer goes out of scope or is overwritten, the object
1006
+ it points to is immediately freed. Effective use of owned boxes can
1007
+ therefore be an efficient alternative to garbage collection.
1020
1008
1021
- ~~~~
1022
- let x = ~5;
1023
- let y = x.clone(); // y is a newly allocated box
1024
- let z = x; // no new memory allocated, x can no longer be used
1025
- ~~~~
1009
+ C++ programmers will recognize ` ~T ` as similar to ` std::unique_ptr<T> `
1010
+ (or ` std::auto_ptr<T> ` in C++03 and below).
1026
1011
1027
- # Borrowed pointers
1012
+ ## Borrowed pointers
1028
1013
1029
- Rust's borrowed pointers are a general purpose reference type. In contrast with
1030
- owned pointers, where the holder of an owned pointer is the owner of the
1031
- pointed-to memory, borrowed pointers never imply ownership. A pointer can be
1032
- borrowed to any object, and the compiler verifies that it cannot outlive the
1033
- lifetime of the object.
1014
+ Rust borrowed pointers are a general purpose reference/pointer type,
1015
+ similar to the C++ reference type, but guaranteed to point to valid
1016
+ memory. In contrast with owned pointers, where the holder of an owned
1017
+ pointer is the owner of the pointed-to memory, borrowed pointers never
1018
+ imply ownership. Pointers may be borrowed from any type, in which case
1019
+ the pointer is guaranteed not to outlive the value it points to.
1034
1020
1035
1021
As an example, consider a simple struct type, ` Point ` :
1036
1022
@@ -1113,23 +1099,7 @@ For a more in-depth explanation of borrowed pointers, read the
1113
1099
1114
1100
[borrowtut]: tutorial-borrowed-ptr.html
1115
1101
1116
- ## Freezing
1117
-
1118
- Borrowing an immutable pointer to an object freezes it and prevents mutation.
1119
- `Owned` objects have freezing enforced statically at compile-time. Mutable
1120
- managed boxes handle freezing dynamically when any of their contents are
1121
- borrowed, and the task will fail if an attempt to modify them is made while
1122
- they are frozen.
1123
-
1124
- ~~~~
1125
- let mut x = 5;
1126
- {
1127
- let y = &x; // x is now frozen, it cannot be modified
1128
- }
1129
- // x is now unfrozen again
1130
- ~~~~
1131
-
1132
- # Dereferencing pointers
1102
+ ## Dereferencing pointers
1133
1103
1134
1104
Rust uses the unary star operator (`*`) to access the contents of a
1135
1105
box or pointer, similarly to C.
0 commit comments