]> git.lizzy.rs Git - rust.git/commitdiff
tutorial: rework the section on destructors
authorDaniel Micay <danielmicay@gmail.com>
Fri, 26 Apr 2013 01:36:02 +0000 (21:36 -0400)
committerDaniel Micay <danielmicay@gmail.com>
Fri, 26 Apr 2013 18:07:47 +0000 (14:07 -0400)
This removes the comparison to manual memory management examples,
because it requires too much existing knowledge. Implementing custom
destructors can be covered in the FFI tutorial, where `unsafe` is
already well explained.

doc/tutorial.md

index c10bc8a294c5d665cae3a1ffb90a36b6d7ebf451..c757329a45f0609ec80a6cd544aac41e24b45775 100644 (file)
@@ -868,108 +868,27 @@ fn first((value, _): (int, float)) -> int { value }
 
 # Destructors
 
-C-style resource management requires the programmer to match every allocation
-with a free, which means manually tracking the responsibility for cleaning up
-(the owner). Correctness is left to the programmer, and it's easy to get wrong.
+A *destructor* is a function responsible for cleaning up the resources used by
+an object when it is no longer accessible. Destructors can be defined to handle
+the release of resources like files, sockets and heap memory.
 
-The following code demonstrates manual memory management, in order to contrast
-it with Rust's resource management. Rust enforces safety, so the `unsafe`
-keyword is used to explicitly wrap the unsafe code. The keyword is a promise to
-the compiler that unsafety does not leak outside of the unsafe block, and is
-used to create safe concepts on top of low-level code.
+Objects are never accessible after their destructor has been called, so there
+are no dynamic failures from accessing freed resources. When a task fails, the
+destructors of all objects in the task are called.
 
-~~~~
-use core::libc::{calloc, free, size_t};
-
-fn main() {
-    unsafe {
-        let a = calloc(1, int::bytes as size_t);
-
-        let d;
+The `~` sigil represents a unique handle for a memory allocation on the heap:
 
-        {
-            let b = calloc(1, int::bytes as size_t);
-
-            let c = calloc(1, int::bytes as size_t);
-            d = c; // move ownership to d
-
-            free(b);
-        }
-
-        free(d);
-        free(a);
-    }
-}
 ~~~~
-
-Rust uses destructors to handle the release of resources like memory
-allocations, files and sockets. An object will only be destroyed when there is
-no longer any way to access it, which prevents dynamic failures from an attempt
-to use a freed resource. When a task fails, the stack unwinds and the
-destructors of all objects owned by that task are called.
-
-The unsafe code from above can be contained behind a safe API that prevents
-memory leaks or use-after-free:
-
-~~~~
-use core::libc::{calloc, free, c_void, size_t};
-
-struct Blob { priv ptr: *c_void }
-
-impl Blob {
-    fn new() -> Blob {
-        unsafe { Blob{ptr: calloc(1, int::bytes as size_t)} }
-    }
-}
-
-impl Drop for Blob {
-    fn finalize(&self) {
-        unsafe { free(self.ptr); }
-    }
-}
-
-fn main() {
-    let a = Blob::new();
-
-    let d;
-
-    {
-        let b = Blob::new();
-
-        let c = Blob::new();
-        d = c; // move ownership to d
-
-        // b is destroyed here
-    }
-
-    // d is destroyed here
-    // a is destroyed here
+{
+    // an integer allocated on the heap
+    let y = ~10;
 }
+// the destructor frees the heap memory as soon as `y` goes out of scope
 ~~~~
 
-This pattern is common enough that Rust includes dynamically allocated memory
-as first-class types (`~` and `@`). Non-memory resources like files are cleaned
-up with custom destructors.
-
-~~~~
-fn main() {
-    let a = ~0;
-
-    let d;
-
-    {
-        let b = ~0;
-
-        let c = ~0;
-        d = c; // move ownership to d
-
-        // b is destroyed here
-    }
-
-    // d is destroyed here
-    // a is destroyed here
-}
-~~~~
+Rust includes syntax for heap memory allocation in the language since it's
+commonly used, but the same semantics can be implemented by a type with a
+custom destructor.
 
 # Ownership
 
@@ -984,6 +903,22 @@ and destroy the contained object when they go out of scope. A box managed by
 the garbage collector starts a new ownership tree, and the destructor is called
 when it is collected.
 
+~~~~
+// the struct owns the objects contained in the `x` and `y` fields
+struct Foo { x: int, y: ~int }
+
+{
+    // `a` is the owner of the struct, and thus the owner of the struct's fields
+    let a = Foo { x: 5, y: ~10 };
+}
+// when `a` goes out of scope, the destructor for the `~int` in the struct's
+// field is called
+
+// `b` is mutable, and the mutability is inherited by the objects it owns
+let mut b = Foo { x: 5, y: ~10 };
+b.x = 10;
+~~~~
+
 If an object doesn't contain garbage-collected boxes, it consists of a single
 ownership tree and is given the `Owned` trait which allows it to be sent
 between tasks. Custom destructors can only be implemented directly on types
@@ -1007,7 +942,7 @@ refer to that through a pointer.
 ## Owned boxes
 
 An owned box (`~`) is a uniquely owned allocation on the heap. It inherits the
-mutability and lifetime of the owner as it would if there was no box.
+mutability and lifetime of the owner as it would if there was no box:
 
 ~~~~
 let x = 5; // immutable
@@ -1021,8 +956,8 @@ let mut y = ~5; // mutable
 
 The purpose of an owned box is to add a layer of indirection in order to create
 recursive data structures or cheaply pass around an object larger than a
-pointer. Since an owned box has a unique owner, it can be used to represent any
-tree data structure.
+pointer. Since an owned box has a unique owner, it can only be used to
+represent a tree data structure.
 
 The following struct won't compile, because the lack of indirection would mean
 it has an infinite size:
@@ -1092,7 +1027,6 @@ d = b;          // box type is the same, okay
 c = b;          // error
 ~~~~
 
-
 # Move semantics
 
 Rust uses a shallow copy for parameter passing, assignment and returning values