]> git.lizzy.rs Git - rust.git/commitdiff
doc: Split out task tutorail. Add links to sub-tutorials
authorBrian Anderson <banderson@mozilla.com>
Sat, 22 Sep 2012 22:33:50 +0000 (15:33 -0700)
committerBrian Anderson <banderson@mozilla.com>
Sat, 22 Sep 2012 22:34:01 +0000 (15:34 -0700)
configure
doc/tutorial-tasks.md [new file with mode: 0644]
doc/tutorial.md
mk/docs.mk
mk/tests.mk

index 980624f0eccd07036c67a4311001ea3a4ef7563c..9f3ecddefe13f1d79eaa250cac9c80caae602fd8 100755 (executable)
--- a/configure
+++ b/configure
@@ -508,6 +508,7 @@ do
     make_dir $h/test/doc-tutorial-ffi
     make_dir $h/test/doc-tutorial-macros
     make_dir $h/test/doc-tutorial-borrowed-ptr
+    make_dir $h/test/doc-tutorial-tasks
     make_dir $h/test/doc-ref
 done
 
diff --git a/doc/tutorial-tasks.md b/doc/tutorial-tasks.md
new file mode 100644 (file)
index 0000000..2f540ef
--- /dev/null
@@ -0,0 +1,173 @@
+% Tasks and communication in Rust
+
+Rust supports a system of lightweight tasks, similar to what is found
+in Erlang or other actor systems. Rust tasks communicate via messages
+and do not share data. However, it is possible to send data without
+copying it by making use of [the exchange heap](#unique-boxes), which
+allow the sending task to release ownership of a value, so that the
+receiving task can keep on using it.
+
+> ***Note:*** As Rust evolves, we expect the task API to grow and
+> change somewhat.  The tutorial documents the API as it exists today.
+
+# Spawning a task
+
+Spawning a task is done using the various spawn functions in the
+module `task`.  Let's begin with the simplest one, `task::spawn()`:
+
+~~~~
+use task::spawn;
+use io::println;
+
+let some_value = 22;
+
+do spawn {
+    println(~"This executes in the child task.");
+    println(fmt!("%d", some_value));
+}
+~~~~
+
+The argument to `task::spawn()` is a [unique
+closure](#unique-closures) of type `fn~()`, meaning that it takes no
+arguments and generates no return value. The effect of `task::spawn()`
+is to fire up a child task that will execute the closure in parallel
+with the creator.
+
+# Communication
+
+Now that we have spawned a child task, it would be nice if we could
+communicate with it. This is done using *pipes*. Pipes are simply a
+pair of endpoints, with one for sending messages and another for
+receiving messages. The easiest way to create a pipe is to use
+`pipes::stream`.  Imagine we wish to perform two expensive
+computations in parallel.  We might write something like:
+
+~~~~
+use task::spawn;
+use pipes::{stream, Port, Chan};
+
+let (chan, port) = stream();
+
+do spawn {
+    let result = some_expensive_computation();
+    chan.send(result);
+}
+
+some_other_expensive_computation();
+let result = port.recv();
+
+# fn some_expensive_computation() -> int { 42 }
+# fn some_other_expensive_computation() {}
+~~~~
+
+Let's walk through this code line-by-line.  The first line creates a
+stream for sending and receiving integers:
+
+~~~~ {.ignore}
+# use pipes::stream;
+let (chan, port) = stream();
+~~~~
+
+This port is where we will receive the message from the child task
+once it is complete.  The channel will be used by the child to send a
+message to the port.  The next statement actually spawns the child:
+
+~~~~
+# use task::{spawn};
+# use comm::{Port, Chan};
+# fn some_expensive_computation() -> int { 42 }
+# let port = Port();
+# let chan = port.chan();
+do spawn {
+    let result = some_expensive_computation();
+    chan.send(result);
+}
+~~~~
+
+This child will perform the expensive computation send the result
+over the channel.  (Under the hood, `chan` was captured by the
+closure that forms the body of the child task.  This capture is
+allowed because channels are sendable.)
+
+Finally, the parent continues by performing
+some other expensive computation and then waiting for the child's result
+to arrive on the port:
+
+~~~~
+# use pipes::{stream, Port, Chan};
+# fn some_other_expensive_computation() {}
+# let (chan, port) = stream::<int>();
+# chan.send(0);
+some_other_expensive_computation();
+let result = port.recv();
+~~~~
+
+# Creating a task with a bi-directional communication path
+
+A very common thing to do is to spawn a child task where the parent
+and child both need to exchange messages with each other. The
+function `std::comm::DuplexStream()` supports this pattern.  We'll
+look briefly at how it is used.
+
+To see how `spawn_conversation()` works, we will create a child task
+that receives `uint` messages, converts them to a string, and sends
+the string in response.  The child terminates when `0` is received.
+Here is the function that implements the child task:
+
+~~~~
+# use std::comm::DuplexStream;
+# use pipes::{Port, Chan};
+fn stringifier(channel: &DuplexStream<~str, uint>) {
+    let mut value: uint;
+    loop {
+        value = channel.recv();
+        channel.send(uint::to_str(value, 10u));
+        if value == 0u { break; }
+    }
+}
+~~~~
+
+The implementation of `DuplexStream` supports both sending and
+receiving. The `stringifier` function takes a `DuplexStream` that can
+send strings (the first type parameter) and receive `uint` messages
+(the second type parameter). The body itself simply loops, reading
+from the channel and then sending its response back.  The actual
+response itself is simply the strified version of the received value,
+`uint::to_str(value)`.
+
+Here is the code for the parent task:
+
+~~~~
+# use std::comm::DuplexStream;
+# use pipes::{Port, Chan};
+# use task::spawn;
+# fn stringifier(channel: &DuplexStream<~str, uint>) {
+#     let mut value: uint;
+#     loop {
+#         value = channel.recv();
+#         channel.send(uint::to_str(value, 10u));
+#         if value == 0u { break; }
+#     }
+# }
+# fn main() {
+
+let (from_child, to_child) = DuplexStream();
+
+do spawn || {
+    stringifier(&to_child);
+};
+
+from_child.send(22u);
+assert from_child.recv() == ~"22";
+
+from_child.send(23u);
+from_child.send(0u);
+
+assert from_child.recv() == ~"23";
+assert from_child.recv() == ~"0";
+
+# }
+~~~~
+
+The parent task first calls `DuplexStream` to create a pair of bidirectional endpoints. It then uses `task::spawn` to create the child task, which captures one end of the communication channel.  As a result, both parent
+and child can send and receive data to and from the other.
index 6d0ddca47bec50ec83b97b8a6ebc5bd68d1cca27..f97fbbed1961cfc9342468ab4504fac02ba6f3e9 100644 (file)
@@ -7,9 +7,11 @@
 This is a tutorial for the Rust programming language. It assumes the
 reader is familiar with the basic concepts of programming, and has
 programmed in one or more other languages before. It will often make
-comparisons to other languages in the C family. The tutorial covers
-the whole language, though not with the depth and precision of the
-[language reference](rust.html).
+comparisons to other languages in the C family. This tutorial covers
+the fundamentals of the language, including the syntax, the type
+system and memory model, and generics.
+[Additional tutorials](#what-next) cover specific language features in
+greater depth.
 
 ## Language overview
 
@@ -2078,257 +2080,30 @@ This makes it possible to rebind a variable without actually mutating
 it, which is mostly useful for destructuring (which can rebind, but
 not assign).
 
-# Tasks
+# What next?
 
-Rust supports a system of lightweight tasks, similar to what is found
-in Erlang or other actor systems. Rust tasks communicate via messages
-and do not share data. However, it is possible to send data without
-copying it by making use of [the exchange heap](#unique-boxes), which
-allow the sending task to release ownership of a value, so that the
-receiving task can keep on using it.
+Now that you know the essentials, check out any of the additional
+tutorials on individual topics.
 
-> ***Note:*** As Rust evolves, we expect the task API to grow and
-> change somewhat.  The tutorial documents the API as it exists today.
+* [Borrowed pointers][borrow]
+* [Tasks and communication][tasks]
+* [Macros][macros]
+* [The foreign function interface][ffi]
 
-## Spawning a task
+There is further documentation on the [wiki], including articles about
+[unit testing] in Rust, [documenting][rustdoc] and [packaging][cargo]
+Rust code, and a discussion of the [attributes] used to apply metada
+to code.
 
-Spawning a task is done using the various spawn functions in the
-module `task`.  Let's begin with the simplest one, `task::spawn()`:
+[borrow]: tutorial-borrowed-ptr.html
+[tasks]: tutorial-tasks.html
+[macros]: tutorial-macros.html
+[ffi]: tutorial-ffi.html
 
-~~~~
-use task::spawn;
-use io::println;
-
-let some_value = 22;
-
-do spawn {
-    println(~"This executes in the child task.");
-    println(fmt!("%d", some_value));
-}
-~~~~
-
-The argument to `task::spawn()` is a [unique
-closure](#unique-closures) of type `fn~()`, meaning that it takes no
-arguments and generates no return value. The effect of `task::spawn()`
-is to fire up a child task that will execute the closure in parallel
-with the creator.
-
-## Communication
-
-Now that we have spawned a child task, it would be nice if we could
-communicate with it. This is done using *pipes*. Pipes are simply a
-pair of endpoints, with one for sending messages and another for
-receiving messages. The easiest way to create a pipe is to use
-`pipes::stream`.  Imagine we wish to perform two expensive
-computations in parallel.  We might write something like:
-
-~~~~
-use task::spawn;
-use pipes::{stream, Port, Chan};
-
-let (chan, port) = stream();
-
-do spawn {
-    let result = some_expensive_computation();
-    chan.send(result);
-}
-
-some_other_expensive_computation();
-let result = port.recv();
-
-# fn some_expensive_computation() -> int { 42 }
-# fn some_other_expensive_computation() {}
-~~~~
-
-Let's walk through this code line-by-line.  The first line creates a
-stream for sending and receiving integers:
-
-~~~~ {.ignore}
-# use pipes::stream;
-let (chan, port) = stream();
-~~~~
-
-This port is where we will receive the message from the child task
-once it is complete.  The channel will be used by the child to send a
-message to the port.  The next statement actually spawns the child:
-
-~~~~
-# use task::{spawn};
-# use comm::{Port, Chan};
-# fn some_expensive_computation() -> int { 42 }
-# let port = Port();
-# let chan = port.chan();
-do spawn {
-    let result = some_expensive_computation();
-    chan.send(result);
-}
-~~~~
-
-This child will perform the expensive computation send the result
-over the channel.  (Under the hood, `chan` was captured by the
-closure that forms the body of the child task.  This capture is
-allowed because channels are sendable.)
-
-Finally, the parent continues by performing
-some other expensive computation and then waiting for the child's result
-to arrive on the port:
-
-~~~~
-# use pipes::{stream, Port, Chan};
-# fn some_other_expensive_computation() {}
-# let (chan, port) = stream::<int>();
-# chan.send(0);
-some_other_expensive_computation();
-let result = port.recv();
-~~~~
-
-## Creating a task with a bi-directional communication path
-
-A very common thing to do is to spawn a child task where the parent
-and child both need to exchange messages with each other. The
-function `std::comm::DuplexStream()` supports this pattern.  We'll
-look briefly at how it is used.
-
-To see how `spawn_conversation()` works, we will create a child task
-that receives `uint` messages, converts them to a string, and sends
-the string in response.  The child terminates when `0` is received.
-Here is the function that implements the child task:
-
-~~~~
-# use std::comm::DuplexStream;
-# use pipes::{Port, Chan};
-fn stringifier(channel: &DuplexStream<~str, uint>) {
-    let mut value: uint;
-    loop {
-        value = channel.recv();
-        channel.send(uint::to_str(value, 10u));
-        if value == 0u { break; }
-    }
-}
-~~~~
-
-The implementation of `DuplexStream` supports both sending and
-receiving. The `stringifier` function takes a `DuplexStream` that can
-send strings (the first type parameter) and receive `uint` messages
-(the second type parameter). The body itself simply loops, reading
-from the channel and then sending its response back.  The actual
-response itself is simply the strified version of the received value,
-`uint::to_str(value)`.
-
-Here is the code for the parent task:
-
-~~~~
-# use std::comm::DuplexStream;
-# use pipes::{Port, Chan};
-# use task::spawn;
-# fn stringifier(channel: &DuplexStream<~str, uint>) {
-#     let mut value: uint;
-#     loop {
-#         value = channel.recv();
-#         channel.send(uint::to_str(value, 10u));
-#         if value == 0u { break; }
-#     }
-# }
-# fn main() {
-
-let (from_child, to_child) = DuplexStream();
-
-do spawn || {
-    stringifier(&to_child);
-};
-
-from_child.send(22u);
-assert from_child.recv() == ~"22";
-
-from_child.send(23u);
-from_child.send(0u);
-
-assert from_child.recv() == ~"23";
-assert from_child.recv() == ~"0";
-
-# }
-~~~~
-
-The parent task first calls `DuplexStream` to create a pair of bidirectional endpoints. It then uses `task::spawn` to create the child task, which captures one end of the communication channel.  As a result, both parent
-and child can send and receive data to and from the other.
-
-# Testing
-
-The Rust language has a facility for testing built into the language.
-Tests can be interspersed with other code, and annotated with the
-`#[test]` attribute.
-
-~~~~{.xfail-test}
-# // FIXME: xfailed because test_twice is a #[test] function it's not
-# // getting compiled
-extern mod std;
-
-fn twice(x: int) -> int { x + x }
-
-#[test]
-fn test_twice() {
-    let mut i = -100;
-    while i < 100 {
-        assert twice(i) == 2 * i;
-        i += 1;
-    }
-}
-~~~~
-
-When you compile the program normally, the `test_twice` function will
-not be included. To compile and run such tests, compile with the
-`--test` flag, and then run the result:
-
-~~~~ {.notrust}
-> rustc --test twice.rs
-> ./twice
-running 1 tests
-test test_twice ... ok
-result: ok. 1 passed; 0 failed; 0 ignored
-~~~~
-
-Or, if we change the file to fail, for example by replacing `x + x`
-with `x + 1`:
-
-~~~~ {.notrust}
-running 1 tests
-test test_twice ... FAILED
-failures:
-    test_twice
-result: FAILED. 0 passed; 1 failed; 0 ignored
-~~~~
-
-You can pass a command-line argument to a program compiled with
-`--test` to run only the tests whose name matches the given string. If
-we had, for example, test functions `test_twice`, `test_once_1`, and
-`test_once_2`, running our program with `./twice test_once` would run
-the latter two, and running it with `./twice test_once_2` would run
-only the last.
-
-To indicate that a test is supposed to fail instead of pass, you can
-give it a `#[should_fail]` attribute.
-
-~~~~
-extern mod std;
-
-fn divide(a: float, b: float) -> float {
-    if b == 0f { fail; }
-    a / b
-}
-
-#[test]
-#[should_fail]
-fn divide_by_zero() { divide(1f, 0f); }
-
-# fn main() { }
-~~~~
-
-To disable a test completely, add an `#[ignore]` attribute. Running a
-test runner (the program compiled with `--test`) with an `--ignored`
-command-line flag will cause it to also run the tests labelled as
-ignored.
+[wiki]: https://github.com/mozilla/rust/wiki/Docs
+[unit testing]: https://github.com/mozilla/rust/wiki/Doc-unit-testing
+[rustdoc]: https://github.com/mozilla/rust/wiki/Doc-using-rustdoc
+[cargo]: https://github.com/mozilla/rust/wiki/Doc-using-cargo-to-manage-packages
+[attributes]: https://github.com/mozilla/rust/wiki/Doc-attributes
 
-A program compiled as a test runner will have the configuration flag
-`test` defined, so that you can add code that won't be included in a
-normal compile with the `#[cfg(test)]` attribute (for a full explanation
-of attributes, see the [language reference](rust.html)).
+[pound-rust]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust
index 084f027b4340b94a4c31f21244127e3ec9df7361..9b2f00923c7a8f3b32bb45c636508c5419d80bf1 100644 (file)
@@ -113,6 +113,16 @@ doc/tutorial-borrowed-ptr.html: tutorial-borrowed-ptr.md doc/version_info.html d
           --include-before-body=doc/version_info.html \
            --output=$@
 
+DOCS += doc/tutorial-tasks.html
+doc/tutorial-tasks.html: tutorial-tasks.md doc/version_info.html doc/rust.css
+       @$(call E, pandoc: $@)
+       $(Q)$(CFG_NODE) $(S)doc/prep.js --highlight $< | \
+          $(CFG_PANDOC) --standalone --toc \
+           --section-divs --number-sections \
+           --from=markdown --to=html --css=rust.css \
+          --include-before-body=doc/version_info.html \
+           --output=$@
+
   endif
 endif
 
index 3b092833cad3e964a74f114e6cffbc5783abdc00..d8457a4f49744f3faaf8dd69b1c73683cca2a3f6 100644 (file)
@@ -179,6 +179,11 @@ doc-tutorial-borrowed-ptr-extract$(1):
        $$(Q)rm -f $(1)/test/doc-tutorial-borrowed-ptr/*.rs
        $$(Q)$$(EXTRACT_TESTS) $$(S)doc/tutorial-borrowed-ptr.md $(1)/test/doc-tutorial-borrowed-ptr
 
+doc-tutorial-tasks-extract$(1):
+       @$$(call E, extract: tutorial-tasks tests)
+       $$(Q)rm -f $(1)/test/doc-tutorial-tasks/*.rs
+       $$(Q)$$(EXTRACT_TESTS) $$(S)doc/tutorial-tasks.md $(1)/test/doc-tutorial-tasks
+
 doc-ref-extract$(1):
        @$$(call E, extract: ref tests)
        $$(Q)rm -f $(1)/test/doc-ref/*.rs
@@ -229,6 +234,7 @@ check-stage$(1)-T-$(2)-H-$(3):                              \
     check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-ffi  \
     check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-macros  \
     check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-borrowed-ptr  \
+    check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-tasks  \
     check-stage$(1)-T-$(2)-H-$(3)-doc-ref
 
 check-stage$(1)-T-$(2)-H-$(3)-core:                            \
@@ -298,6 +304,9 @@ check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-macros: \
 check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-borrowed-ptr: \
        check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-borrowed-ptr-dummy
 
+check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-tasks: \
+       check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-tasks-dummy
+
 check-stage$(1)-T-$(2)-H-$(3)-doc-ref: \
        check-stage$(1)-T-$(2)-H-$(3)-doc-ref-dummy
 
@@ -485,6 +494,12 @@ DOC_TUTORIAL_BORROWED_PTR_ARGS$(1)-T-$(2)-H-$(3) :=        \
         --build-base $(3)/test/doc-tutorial-borrowed-ptr/ \
         --mode run-pass
 
+DOC_TUTORIAL_TASKS_ARGS$(1)-T-$(2)-H-$(3) :=   \
+               $$(CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3)) \
+        --src-base $(3)/test/doc-tutorial-tasks/       \
+        --build-base $(3)/test/doc-tutorial-tasks/ \
+        --mode run-pass
+
 DOC_REF_ARGS$(1)-T-$(2)-H-$(3) :=                      \
                $$(CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3)) \
         --src-base $(3)/test/doc-ref/                  \
@@ -611,6 +626,14 @@ check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-borrowed-ptr-dummy:       \
                 $$(DOC_TUTORIAL_BORROWED_PTR_ARGS$(1)-T-$(2)-H-$(3)) \
                --logfile tmp/check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-borrowed-ptr.log
 
+check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-tasks-dummy:       \
+               $$(TEST_SREQ$(1)_T_$(2)_H_$(3))         \
+                doc-tutorial-tasks-extract$(3)
+       @$$(call E, run doc-tutorial-tasks: $$<)
+       $$(Q)$$(call CFG_RUN_CTEST,$(1),$$<,$(3)) \
+                $$(DOC_TUTORIAL_TASKS_ARGS$(1)-T-$(2)-H-$(3)) \
+               --logfile tmp/check-stage$(1)-T-$(2)-H-$(3)-doc-tutorial-tasks.log
+
 check-stage$(1)-T-$(2)-H-$(3)-doc-ref-dummy:            \
                $$(TEST_SREQ$(1)_T_$(2)_H_$(3))         \
                 doc-ref-extract$(3)
@@ -748,6 +771,9 @@ check-stage$(1)-H-$(2)-doc-tutorial-macros:                 \
 check-stage$(1)-H-$(2)-doc-tutorial-borrowed-ptr:              \
        $$(foreach target,$$(CFG_TARGET_TRIPLES),       \
         check-stage$(1)-T-$$(target)-H-$(2)-doc-tutorial-borrowed-ptr)
+check-stage$(1)-H-$(2)-doc-tutorial-tasks:             \
+       $$(foreach target,$$(CFG_TARGET_TRIPLES),       \
+        check-stage$(1)-T-$$(target)-H-$(2)-doc-tutorial-tasks)
 check-stage$(1)-H-$(2)-doc-ref:                                \
        $$(foreach target,$$(CFG_TARGET_TRIPLES),       \
         check-stage$(1)-T-$$(target)-H-$(2)-doc-ref)
@@ -859,6 +885,7 @@ check-stage$(1)-doc-tutorial: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial
 check-stage$(1)-doc-tutorial-ffi: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial-ffi
 check-stage$(1)-doc-tutorial-macros: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial-macros
 check-stage$(1)-doc-tutorial-borrowed-ptr: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial-borrowed-ptr
+check-stage$(1)-doc-tutorial-tasks: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-tutorial-tasks
 check-stage$(1)-doc-ref: check-stage$(1)-H-$$(CFG_HOST_TRIPLE)-doc-ref
 
 endef