@@ -853,170 +853,184 @@ 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
- # 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.
856
+ # Destructors
889
857
890
- [ borrow ] : tutorial-borrowed-ptr.html
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.
891
861
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.
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.
900
867
901
- All pointer types can be dereferenced with the ` * ` unary operator.
868
+ ~~~~
869
+ use core::libc::funcs::c95::stdlib::{calloc, free};
870
+ use core::libc::types::os::arch::c95::size_t;
902
871
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.
872
+ fn main() {
873
+ unsafe {
874
+ let a = calloc(1, int::bytes as size_t);
907
875
908
- ## Managed boxes
876
+ let d;
909
877
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.
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
883
+
884
+ free(b);
885
+ }
915
886
887
+ free(d);
888
+ free(a);
889
+ }
890
+ }
916
891
~~~~
917
- let x: @int = @10; // New box
918
- let y = x; // Copy of a pointer to the same box
919
892
920
- // x and y both refer to the same allocation. When both go out of scope
921
- // then the allocation will be freed.
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
+
922
902
~~~~
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;
923
906
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.
907
+ struct Blob { priv ptr: *c_void }
926
908
927
- ~~~
928
- // A linked list node
929
- struct Node {
930
- next: MaybeNode,
931
- prev: MaybeNode,
932
- payload: int
909
+ impl Blob {
910
+ static fn new() -> Blob {
911
+ unsafe { Blob{ptr: calloc(1, int::bytes as size_t)} }
912
+ }
933
913
}
934
914
935
- enum MaybeNode {
936
- SomeNode(@mut Node),
937
- NoNode
915
+ impl Drop for Blob {
916
+ fn finalize(&self) {
917
+ unsafe { free(self.ptr); }
918
+ }
938
919
}
939
920
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 };
921
+ fn main() {
922
+ let a = Blob::new();
943
923
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
- ~~~
924
+ let d;
950
925
951
- Managed boxes never cross task boundaries. This has several benefits for
952
- performance:
926
+ {
927
+ let b = Blob::new();
953
928
954
- * The Rust garbage collector does not need to stop multiple threads in order
955
- to collect garbage.
929
+ let c = Blob::new();
930
+ d = c; // move ownership to d
956
931
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.
932
+ // b is destroyed here
933
+ }
960
934
961
- C++ programmers will recognize ` @T ` as similar to ` std::shared_ptr<T> ` .
935
+ // d is destroyed here
936
+ // a is destroyed here
937
+ }
938
+ ~~~~
962
939
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 .
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 .
966
943
967
- ## Owned boxes
944
+ ~~~~
945
+ fn main() {
946
+ let a = ~0;
968
947
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.
948
+ let d;
974
949
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.
950
+ {
951
+ let b = ~0;
981
952
982
- ~~~~
983
- let x = ~10;
984
- // Move x to y, deinitializing x
985
- let y = x;
986
- ~~~~
953
+ let c = ~0;
954
+ d = c; // move ownership to d
987
955
988
- If you really want to copy an owned box you must say so explicitly.
956
+ // b is destroyed here
957
+ }
989
958
959
+ // d is destroyed here
960
+ // a is destroyed here
961
+ }
990
962
~~~~
991
- let x = ~10;
992
- let y = copy x;
993
963
994
- let z = *x + *y;
995
- fail_unless!(z == 20);
996
- ~~~~
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.
997
1002
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.
1003
+ ## Managed boxes
1004
+
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.
1012
+
1013
+ # Move semantics
1004
1014
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.
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.
1008
1020
1009
- C++ programmers will recognize ` ~T ` as similar to ` std::unique_ptr<T> `
1010
- (or ` std::auto_ptr<T> ` in C++03 and below).
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
+ ~~~~
1011
1026
1012
- ## Borrowed pointers
1027
+ # Borrowed pointers
1013
1028
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.
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.
1020
1034
1021
1035
As an example, consider a simple struct type, ` Point ` :
1022
1036
@@ -1099,7 +1113,23 @@ For a more in-depth explanation of borrowed pointers, read the
1099
1113
1100
1114
[borrowtut]: tutorial-borrowed-ptr.html
1101
1115
1102
- ## Dereferencing pointers
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
1103
1133
1104
1134
Rust uses the unary star operator (`*`) to access the contents of a
1105
1135
box or pointer, similarly to C.
0 commit comments