about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-04-27 12:09:35 -0700
committerbors <bors@rust-lang.org>2013-04-27 12:09:35 -0700
commit5de74f3e754628fa98c0cdf0b95b39f39db50e19 (patch)
tree617c8236034c826be26df695b3d1a159207bba77
parent9ea32a380d84affbe979b91980f8179cee8925b9 (diff)
parent195911fca412527472233aee163782c3bca11517 (diff)
downloadrust-5de74f3e754628fa98c0cdf0b95b39f39db50e19.tar.gz
rust-5de74f3e754628fa98c0cdf0b95b39f39db50e19.zip
auto merge of #6070 : thestinger/rust/tutorial, r=pcwalton
-rw-r--r--doc/tutorial-ffi.md68
-rw-r--r--doc/tutorial.md132
2 files changed, 101 insertions, 99 deletions
diff --git a/doc/tutorial-ffi.md b/doc/tutorial-ffi.md
index 6bebc6fd33a..127f8158923 100644
--- a/doc/tutorial-ffi.md
+++ b/doc/tutorial-ffi.md
@@ -139,6 +139,74 @@ pub fn uncompress(src: &[u8]) -> Option<~[u8]> {
 For reference, the examples used here are also available as an [library on
 GitHub](https://github.com/thestinger/rust-snappy).
 
+# Destructors
+
+Foreign libraries often hand off ownership of resources to the calling code,
+which should be wrapped in a destructor to provide safety and guarantee their
+release.
+
+A type with the same functionality as owned boxes can be implemented by
+wrapping `malloc` and `free`:
+
+~~~~
+use core::libc::{c_void, size_t, malloc, free};
+
+#[abi = "rust-intrinsic"]
+extern "rust-intrinsic" mod rusti {
+    fn init<T>() -> T;
+}
+
+// a wrapper around the handle returned by the foreign code
+pub struct Unique<T> {
+    priv ptr: *mut T
+}
+
+pub impl<'self, T: Owned> Unique<T> {
+    fn new(value: T) -> Unique<T> {
+        unsafe {
+            let ptr = malloc(core::sys::size_of::<T>() as size_t) as *mut T;
+            assert!(!ptr::is_null(ptr));
+            *ptr = value;
+            Unique{ptr: ptr}
+        }
+    }
+
+    // the 'self lifetime results in the same semantics as `&*x` with ~T
+    fn borrow(&self) -> &'self T {
+        unsafe { cast::transmute(self.ptr) }
+    }
+
+    // the 'self lifetime results in the same semantics as `&mut *x` with ~T
+    fn borrow_mut(&mut self) -> &'self mut T {
+        unsafe { cast::transmute(self.ptr) }
+    }
+}
+
+#[unsafe_destructor]
+impl<T: Owned> Drop for Unique<T> {
+    fn finalize(&self) {
+        unsafe {
+            let mut x = rusti::init(); // dummy value to swap in
+            x <-> *self.ptr; // moving the object out is needed to call the destructor
+            free(self.ptr as *c_void)
+        }
+    }
+}
+
+// A comparison between the built-in ~ and this reimplementation
+fn main() {
+    {
+        let mut x = ~5;
+        *x = 10;
+    } // `x` is freed here
+
+    {
+        let mut y = Unique::new(5);
+        *y.borrow_mut() = 10;
+    } // `y` is freed here
+}
+~~~~
+
 # Linking
 
 In addition to the `#[link_args]` attribute for explicitly passing arguments to the linker, an
diff --git a/doc/tutorial.md b/doc/tutorial.md
index c10bc8a294c..c757329a45f 100644
--- a/doc/tutorial.md
+++ b/doc/tutorial.md
@@ -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