about summary refs log tree commit diff
path: root/doc/tutorial.md
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-08-31 16:57:37 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-08-31 16:58:23 -0700
commit9eaaceb9f020faca7e2880fdc94946056c0ce4cf (patch)
tree418038089da67aa49658394bc79a107ed053a4e0 /doc/tutorial.md
parent329281ebcc3a6e007b33607b7376602a7a6e11ec (diff)
downloadrust-9eaaceb9f020faca7e2880fdc94946056c0ce4cf.tar.gz
rust-9eaaceb9f020faca7e2880fdc94946056c0ce4cf.zip
doc: Update the tutorial
Diffstat (limited to 'doc/tutorial.md')
-rw-r--r--doc/tutorial.md621
1 files changed, 180 insertions, 441 deletions
diff --git a/doc/tutorial.md b/doc/tutorial.md
index 5b5623020c8..0fc56f135c9 100644
--- a/doc/tutorial.md
+++ b/doc/tutorial.md
@@ -69,61 +69,6 @@ C. As will become clear in the rest of this tutorial, it goes in quite
 a different direction, with efficient, strongly-typed and memory-safe
 support for many high-level idioms.
 
-Here's a parallel game of rock, paper, scissors to whet your appetite.
-
-~~~~
-use std;
-
-import pipes::PortSet;
-import task::spawn;
-import iter::repeat;
-import rand::{seeded_rng, seed};
-import uint::range;
-import io::println;
-
-fn main() {
-    // Open a channel to receive game results
-    let result_from_game = PortSet();
-    let times = 10;
-    let player1 = ~"graydon";
-    let player2 = ~"patrick";
-
-    for repeat(times) {
-        // Start another task to play the game
-        let result = result_from_game.chan();
-        do spawn |copy player1, copy player2| {
-            let outcome = play_game(player1, player2);
-            result.send(outcome);
-        }
-    }
-
-    // Report the results as the games complete
-    for range(0, times) |round| {
-        let winner = result_from_game.recv();
-        println(fmt!("%s wins round #%u", winner, round));
-    }
-
-    fn play_game(player1: ~str, player2: ~str) -> ~str {
-
-        // Our rock/paper/scissors types
-        enum gesture {
-            rock, paper, scissors
-        }
-
-        let rng = seeded_rng(seed());
-        // A small inline function for picking an RPS gesture
-        let pick = || (~[rock, paper, scissors])[rng.gen_uint() % 3];
-
-        // Pick two gestures and decide the result
-        match (pick(), pick()) {
-            (rock, scissors) | (paper, rock) | (scissors, paper) => copy player1,
-            (scissors, rock) | (rock, paper) | (paper, scissors) => copy player2,
-            _ => ~"tie"
-        }
-    }
-}
-~~~~
-
 ## Conventions
 
 Throughout the tutorial, words that indicate language keywords or
@@ -207,8 +152,8 @@ Rust program files are, by convention, given the extension `.rs`. Say
 we have a file `hello.rs` containing this program:
 
 ~~~~
-fn main(args: ~[~str]) {
-    io::println(~"hello world from '" + args[0] + ~"'!");
+fn main() {
+    io::println("hello world!");
 }
 ~~~~
 
@@ -221,7 +166,7 @@ If you modify the program to make it invalid (for example, by changing
 
 ~~~~ {.notrust}
 hello.rs:2:4: 2:16 error: unresolved name: io::print_it
-hello.rs:2     io::print_it(~"hello world from '" + args[0] + ~"'!");
+hello.rs:2     io::print_it("hello world!");
                ^~~~~~~~~~~~
 ~~~~
 
@@ -237,7 +182,7 @@ declaration to appear at the top level of the file—all statements must
 live inside a function.
 
 Rust programs can also be compiled as libraries, and included in other
-programs. The `use std` directive that appears at the top of a lot of
+programs. The `extern mod std` directive that appears at the top of a lot of
 examples imports the [standard library][std]. This is described in more
 detail [later on](#modules-and-crates).
 
@@ -262,13 +207,6 @@ difference to be aware of is that the bodies of `if` statements and of
 `while` loops *have* to be wrapped in brackets. Single-statement,
 bracket-less bodies are not allowed.
 
-If the verbosity of that bothers you, consider the fact that this
-allows you to omit the parentheses around the condition in `if`,
-`while`, and similar constructs. This will save you two characters
-every time. As a bonus, you no longer have to spend any mental energy
-on deciding whether you need to add braces or not, or on adding them
-after the fact when adding a statement to an `if` branch.
-
 Accounting for these differences, the surface syntax of Rust
 statements and expressions is C-like. Function calls are written
 `myfunc(arg1, arg2)`, operators have mostly the same name and
@@ -276,14 +214,18 @@ precedence that they have in C, comments look the same, and constructs
 like `if` and `while` are available:
 
 ~~~~
-# fn call_a_function(_a: int) {}
+# fn it_works() {}
+# fn abort() {}
 fn main() {
-    if 1 < 2 {
-        while false { call_a_function(10 * 4); }
-    } else if 4 < 3 || 3 < 4 {
-        // Comments are C++-style too
-    } else {
-        /* Multi-line comment syntax */
+    while true {
+        /* Ensure that basic math works. */
+        if 2*20 > 30 {
+            // Everything is OK.
+            it_works();
+        } else {
+            abort();
+        }
+        break;
     }
 }
 ~~~~
@@ -291,36 +233,45 @@ fn main() {
 ## Expression syntax
 
 Though it isn't apparent in all code, there is a fundamental
-difference between Rust's syntax and the predecessors in this family
-of languages. A lot of things that are statements in C are expressions
-in Rust. This allows for useless things like this (which passes
-nil—the void type—to a function):
-
-~~~~
-# fn a_function(_a: ()) {}
-a_function(while false {});
+difference between Rust's syntax and its predecessors in this family
+of languages. Many constructs that are statements in C are expressions
+in Rust. This allows Rust to be more expressive. For example, you might
+write a piece of code like this:
+
+~~~~
+# let item = "salad";
+let price;
+if item == "salad" {
+    price = 3.50;
+} else if item == "muffin" {
+    price = 2.25;
+} else {
+    price = 2.00;
+}
 ~~~~
 
-But also useful things like this:
+But, in Rust, you don't have to repeat the name `price`:
 
 ~~~~
-# fn the_stars_align() -> bool { false }
-# fn something_else() -> bool { true }
-let x = if the_stars_align() { 4 }
-        else if something_else() { 3 }
-        else { 0 };
+# let item = "salad";
+let price = if item == "salad" { 3.50 }
+            else if item == "muffin" { 2.25 }
+            else { 2.00 };
 ~~~~
 
-This piece of code will bind the variable `x` to a value depending on
-the conditions. Note the condition bodies, which look like `{
-expression }`. The lack of a semicolon after the last statement in a
-braced block gives the whole block the value of that last expression.
-If the branches of the `if` had looked like `{ 4; }`, the above
-example would simply assign nil (void) to `x`. But without the
-semicolon, each branch has a different value, and `x` gets the value
-of the branch that was taken.
+Both pieces of code are exactly equivalent—they assign a value to `price`
+depending on the condition that holds. Note that the semicolons are omitted
+from the second snippet. This is important; the lack of a semicolon after the
+last statement in a braced block gives the whole block the value of that last
+expression.
+
+Put another way, the semicolon in Rust *ignores the value of an expression*.
+Thus, if the branches of the `if` had looked like `{ 4; }`, the above example
+would simply assign nil (void) to `price`. But without the semicolon, each
+branch has a different value, and `price` gets the value of the branch that
+was taken.
 
-This also works for function bodies. This function returns a boolean:
+This feature also works for function bodies. This function returns a boolean:
 
 ~~~~
 fn is_four(x: int) -> bool { x == 4 }
@@ -341,9 +292,12 @@ like the `let x = ...` example above.
 
 ## Identifiers
 
-Rust identifiers must start with an alphabetic character or an
-underscore, and after that may contain any alphanumeric character, and
-more underscores.
+Rust identifiers follow the same rules as C; they start with an alphabetic
+character or an underscore, and after that may contain any sequence of
+alphabetic characters, numbers, or underscores. The preferred style is to
+begin function, variable, and module names with a lowercase letter, using
+underscores where they help readability, while beginning types with a capital
+letter.
 
 The double-colon (`::`) is used as a module separator, so
 `io::println` means 'the thing named `println` in the module
@@ -365,20 +319,19 @@ a local variable that can be reassigned.  Global constants can be
 defined with `const`:
 
 ~~~~
-use std;
-const repeat: uint = 5u;
+const repeat: int = 5;
 fn main() {
-    let hi = ~"Hi!";
-    let mut count = 0u;
+    let hi = "Hi!";
+    let mut count = 0;
     while count < repeat {
         io::println(hi);
-        count += 1u;
+        count += 1;
     }
 }
 ~~~~
 
-Local variables may shadow earlier declarations, causing the
-previous variable to go out of scope.
+Local variables may shadow earlier declarations, making the earlier variables
+inaccessible.
 
 ~~~~
 let my_favorite_value: float = 57.8;
@@ -403,10 +356,10 @@ annotation:
 
 ~~~~
 // The type of this vector will be inferred based on its use.
-let x = ~[];
+let x = [];
 # vec::map(x, fn&(&&_y:int) -> int { _y });
-// Explicitly say this is a vector of integers.
-let y: ~[int] = ~[];
+// Explicitly say this is a vector of zero integers.
+let y: [int * 0] = [];
 ~~~~
 
 The basic types are written like this:
@@ -429,49 +382,58 @@ The basic types are written like this:
 `u8`, `u16`, `u32`, `u64`
   : Unsigned integers with a specific size.
 
-`f32`, `f64`
-  : Floating-point types.
-
 `float`
-  : The largest floating-point type efficiently supported on the target machine.
+  : The largest floating-point type efficiently supported on the target
+    machine.
 
-`char`
-  : A character is a 32-bit Unicode code point.
+`f32`, `f64`
+  : Floating-point types with a specific size.
 
-`~str`
-  : String type. A string contains a UTF-8 encoded sequence of characters.
+`char`
+  : A Unicode character (32 bits).
 
 These can be combined in composite types, which will be described in
 more detail later on (the `T`s here stand for any other type):
 
-`~[T]`
-  : Vector type.
+`[T * N]`
+  : Vector (like an array in other languages) with N elements.
 
-`~[mut T]`
-  : Mutable vector type.
+`[mut T * N]`
+  : Mutable vector with N elements.
 
 `(T1, T2)`
   : Tuple type. Any arity above 1 is supported.
 
-`{field1: T1, field2: T2}`
-  : Record type.
+`@T`, `~T`, `&T`
+  : Pointer types.
+
+The size of some types can vary when your program runs. Because of this, you
+don't manipulate them only by pointer, never directly. For instance, you 
+can't refer to a string (`str`); instead you refer to a pointer to a string
+(`@str`, `~str`, or `&str`). These *dynamically-sized* types const of:
 
-`fn(arg1: T1, arg2: T2) -> T3`, `fn@()`, `fn~()`, `fn&()`
+`fn(arg1: T1, arg2: T2) -> T3`
   : Function types.
 
-`@T`, `~T`, `*T`
-  : Pointer types.
+`str`
+  : String type. A string contains a UTF-8 encoded sequence of characters.
+
+`[T]`
+  : Vector with unknown size (also called a slice).
+
+`[mut T]`
+  : Mutable vector with unknown size.
 
 Types can be given names with `type` declarations:
 
 ~~~~
-type monster_size = uint;
+type MonsterSize = uint;
 ~~~~
 
-This will provide a synonym, `monster_size`, for unsigned integers. It
-will not actually create a new type—`monster_size` and `uint` can be
-used interchangeably, and using one where the other is expected is not
-a type error. Read about [single-variant enums](#single_variant_enum)
+This will provide a synonym, `MonsterSize`, for unsigned integers. It will not
+actually create a new, incompatible type—`MonsterSize` and `uint` can be used
+interchangeably, and using one where the other is expected is not a type
+error. Read about [single-variant enums](#single_variant_enum)
 further on if you need to create a type name that's not just a
 synonym.
 
@@ -482,107 +444,46 @@ binary (`0b10010000`) base.
 
 If you write an integer literal without a suffix (`3`, `-500`, etc.),
 the Rust compiler will try to infer its type based on type annotations
-and function signatures in the surrounding program.  For example, here
-the type of `x` is inferred to be `u16` because it is passed to a
-function that takes a `u16` argument:
-
-~~~~
-let x = 3;
-
-fn identity_u16(n: u16) -> u16 { n }
-
-identity_u16(x);
-~~~~
-
-On the other hand, if the program gives conflicting information about
-what the type of the unsuffixed literal should be, you'll get an error
-message.
-
-~~~~{.xfail-test}
-let x = 3;
-let y: i32 = 3;
-
-fn identity_u8(n: u8) -> u8 { n }
-fn identity_u16(n: u16) -> u16 { n }
+and function signatures in the surrounding program. In the absence of any type
+annotations at all, Rust will assume that an unsuffixed integer literal has
+type `int`. It's also possible to avoid any type ambiguity by writing integer
+literals with a suffix. For example:
 
-identity_u8(x);  // after this, `x` is assumed to have type `u8`
-identity_u16(x); // raises a type error (expected `u16` but found `u8`)
-identity_u16(y); // raises a type error (expected `u16` but found `i32`)
 ~~~~
-
-In the absence of any type annotations at all, Rust will assume that
-an unsuffixed integer literal has type `int`.
-
+let x = 50;
+log(error, x); // x is an int
+let y = 100u;
+log(error, y); // y is an uint
 ~~~~
-let n = 50;
-log(error, n); // n is an int
-~~~~
-
-It's also possible to avoid any type ambiguity by writing integer
-literals with a suffix.  The suffixes `i` and `u` are for the types
-`int` and `uint`, respectively: the literal `-3i` has type `int`,
-while `127u` has type `uint`.  For the fixed-size integer types, just
-suffix the literal with the type name: `255u8`, `50i64`, etc.
 
 Note that, in Rust, no implicit conversion between integer types
 happens. If you are adding one to a variable of type `uint`, saying
 `+= 1u8` will give you a type error.
 
 Floating point numbers are written `0.0`, `1e6`, or `2.1e-4`. Without
-a suffix, the literal is assumed to be of type `float`. Suffixes `f32`
-and `f64` can be used to create literals of a specific type. The
-suffix `f` can be used to write `float` literals without a dot or
-exponent: `3f`.
+a suffix, the literal is assumed to be of type `float`. Suffixes `f` (32-bit)
+and `l` (64-bit) can be used to create literals of a specific type.
 
 ## Other literals
 
 The nil literal is written just like the type: `()`. The keywords
 `true` and `false` produce the boolean literals.
 
-Character literals are written between single quotes, as in `'x'`. You
-may put non-ascii characters between single quotes (your source files
-should be encoded as UTF-8). Rust understands a number of
-character escapes, using the backslash character:
-
-`\n`
-  : A newline (Unicode character 10).
-
-`\r`
-  : A carriage return (13).
-
-`\t`
-  : A tab character (9).
-
-`\\`, `\'`, `\"`
-  : Simply escapes the following character.
-
-`\xHH`, `\uHHHH`, `\UHHHHHHHH`
-  : Unicode escapes, where the `H` characters are the hexadecimal digits that
-    form the character code.
+Character literals are written between single quotes, as in `'x'`. Just as in
+C, Rust understands a number of character escapes, using the backslash
+character, `\n`, `\r`, and `\t` being the most common.
 
 String literals allow the same escape sequences. They are written
-between double quotes (`~"hello"`). Rust strings may contain newlines.
-When a newline is preceded by a backslash, it, and all white space
-following it, will not appear in the resulting string literal. So
-this is equivalent to `~"abc"`:
-
-~~~~
-let s = ~"a\
-         b\
-         c";
-~~~~
+between double quotes (`"hello"`). Rust strings may contain newlines.
 
 ## Operators
 
 Rust's set of operators contains very few surprises. Binary arithmetic
 is done with `*`, `/`, `%`, `+`, and `-` (multiply, divide, remainder,
-plus, minus). `-` is also a unary prefix operator that does negation.
+plus, minus). `-` is also a unary prefix operator that does negation. As in C,
+the bit operators `>>`, `<<`, `&`, `|`, and `^` are supported.
 
-Binary shifting is done with `>>` (shift right), and `<<` (shift
-left). Shift right is arithmetic if the value is signed and logical if
-the value is unsigned. Logical bitwise operators are `&`, `|`, and `^`
-(and, or, and exclusive or), and unary `!` for bitwise negation (or
-boolean negation when applied to a boolean value).
+Note that, if applied an integer value, `!` inverts all the bits.
 
 The comparison operators are the traditional `==`, `!=`, `<`, `>`,
 `<=`, and `>=`. Short-circuiting (lazy) boolean operators are written
@@ -605,46 +506,13 @@ the logical bitwise operators have higher precedence — in C, `x & 2 > 0`
 comes out as `x & (2 > 0)`, in Rust, it means `(x & 2) > 0`, which is
 more likely to be what you expect (unless you are a C veteran).
 
-## Attributes
-
-Every definition can be annotated with attributes. Attributes are meta
-information that can serve a variety of purposes. One of those is
-conditional compilation:
-
-~~~~
-#[cfg(windows)]
-fn register_win_service() { /* ... */ }
-~~~~
-
-This will cause the function to vanish without a trace during
-compilation on a non-Windows platform, much like `#ifdef` in C.
-
-Attributes are always wrapped in hash-braces (`#[attr]`). Inside the
-braces, a small minilanguage is supported, whose interpretation
-depends on the attribute that's being used. The simplest form is a
-plain name (as in `#[test]`, which is used by the [built-in test
-framework](#testing)). A name-value pair can be provided using an `=`
-character followed by a literal (as in `#[license = "BSD"]`, which is
-a valid way to annotate a Rust program as being released under a
-BSD-style license). Finally, you can have a name followed by a
-comma-separated list of nested attributes, as in this
-[crate](#modules-and-crates) metadata declaration:
-
-~~~~ {.ignore}
-#[link(name = "std",
-       vers = "0.1",
-       url = "http://rust-lang.org/src/std")];
-~~~~
-
-An attribute without a semicolon following it applies to the
-definition that follows it. When terminated with a semicolon, it
-applies to the module or crate in which it appears.
-
 ## Syntax extensions
 
-The compiler defines a few built-in syntax extensions. The most useful
-one is `fmt!`, a sprintf-style text formatter that is expanded
-at compile time.
+*Syntax extensions* are special syntax that is not built into the language,
+but are instead provided by the libraries. To make it clear when a syntax
+extension is being used, their names all end with `!`. The standard library
+defines a few syntax extensions. The most useful one is `fmt!`, a
+`sprintf`-style text formatter that is expanded at compile time.
 
 ~~~~
 io::println(fmt!("%s is %d", ~"the answer", 42));
@@ -656,16 +524,7 @@ don't match the types of the arguments.
 
 [pf]: http://en.cppreference.com/w/cpp/io/c/fprintf
 
-All syntax extensions look like `extension_name!`. Another built-in one is
-`env!`, which will look up its argument as an environment variable at
-compile-time.
-
-~~~~
-io::println(env!("PATH"));
-~~~~
-
-It is possible for the user to define new syntax extensions, within certain
-limits. These are called [macros](#macros).
+You can define your own syntax extensions via macros.
 
 # Control structures
 
@@ -694,30 +553,24 @@ end of the block:
 fn signum(x: int) -> int {
     if x < 0 { -1 }
     else if x > 0 { 1 }
-    else { return 0; }
+    else { return 0 }
 }
 ~~~~
 
-The `return` and its semicolon could have been left out without
-changing the meaning of this function, but it illustrates that you
-will not get a type error in this case, although the last arm doesn't
-have type `int`, because control doesn't reach the end of that arm
-(`return` is jumping out of the function).
-
 ## Pattern matching
 
 Rust's `match` construct is a generalized, cleaned-up version of C's
-`switch` construct. You provide it with a value and a number of arms,
-each labelled with a pattern, and it will execute the arm that matches
-the value.
+`switch` construct. You provide it with a value and a number of *arms*,
+each labelled with a pattern, and the code will attempt to match each pattern
+in order. For the first one that matches, the arm is executed.
 
 ~~~~
 # let my_number = 1;
 match my_number {
-  0       => io::println(~"zero"),
-  1 | 2   => io::println(~"one or two"),
-  3 to 10 => io::println(~"three to ten"),
-  _       => io::println(~"something else")
+  0     => io::println("zero"),
+  1 | 2 => io::println("one or two"),
+  3..10 => io::println("three to ten"),
+  _     => io::println("something else")
 }
 ~~~~
 
@@ -725,10 +578,10 @@ There is no 'falling through' between arms, as in C—only one arm is
 executed, and it doesn't have to explicitly `break` out of the
 construct when it is finished.
 
-The part to the left of each arm is called the pattern. Literals are
-valid patterns, and will match only their own value. The pipe operator
+The part to the left of the arrow `=>` is called the *pattern*. Literals are
+valid patterns and will match only their own value. The pipe operator
 (`|`) can be used to assign multiple patterns to a single arm. Ranges
-of numeric literal patterns can be expressed with `to`. The underscore
+of numeric literal patterns can be expressed with `..`. The underscore
 (`_`) is a wildcard pattern that matches everything.
 
 The patterns in an match arm are followed by a fat arrow, `=>`, then an
@@ -740,10 +593,10 @@ commas are optional.
 # let my_number = 1;
 match my_number {
   0 => {
-    io::println(~"zero")
+    io::println("zero")
   }
   _ => {
-    io::println(~"something else")
+    io::println("something else")
   }
 }
 ~~~
@@ -758,10 +611,11 @@ you use the matching to get at the contents of data types. Remember
 that `(float, float)` is a tuple of two floats:
 
 ~~~~
-fn angle(vec: (float, float)) -> float {
-    match vec {
-      (0f, y) if y < 0f => 1.5 * float::consts::pi,
-      (0f, y) => 0.5 * float::consts::pi,
+use float::consts::pi;
+fn angle(vector: (float, float)) -> float {
+    match vector {
+      (0f, y) if y < 0f => 1.5 * pi,
+      (0f, y) => 0.5 * pi,
       (x, y) => float::atan(y / x)
     }
 }
@@ -828,88 +682,6 @@ For more involved iteration, such as going over the elements of a
 collection, Rust uses higher-order functions. We'll come back to those
 in a moment.
 
-## Failure
-
-The `fail` keyword causes the current [task](#tasks) to fail. You use
-it to indicate unexpected failure, much like you'd use `abort` in a
-C program or a fatal exception in a C++ program.
-
-There is no way for the current task to resume execution after
-failure; failure is nonrecoverable. It is, however, possible for
-*another* task to handle the failure, allowing the program to continue
-running.
-
-`fail` takes an optional argument specifying the reason for the
-failure. It must have type `~str`.
-
-In addition to the `fail` statement, the following circumstances cause
-task failure:
-
-* Accessing an out-of-bounds element of a vector.
-
-* An assertion failure.
-
-* Integer division by zero.
-
-* Running out of memory.
-
-## Assertions
-
-The keyword `assert`, followed by an expression with boolean type,
-will check that the given expression results in `true`, and cause a
-failure otherwise. It is typically used to double-check things that
-*should* hold at a certain point in a program. `assert` statements are
-always active; there is no way to build Rust code with assertions
-disabled.
-
-~~~~
-let mut x = 100;
-while (x > 10) { x -= 10; }
-assert x == 10;
-~~~~
-
-## Logging
-
-Rust has a built-in logging mechanism, using the `log` statement.
-Logging is polymorphic—any type of value can be logged, and the
-runtime will do its best to output a textual representation of the
-value.
-
-~~~~
-log(warn, ~"hi");
-log(error, (1, ~[2.5, -1.8]));
-~~~~
-
-The first argument is the log level (levels `debug`, `info`, `warn`,
-and `error` are predefined), and the second is the value to log. By
-default, you *will not* see the output of that first log statement,
-which has `warn` level. The environment variable `RUST_LOG` controls
-which log level is used. It can contain a comma-separated list of
-paths for modules that should be logged. For example, running `rustc`
-with `RUST_LOG=rustc::front::attr` will turn on logging in its
-attribute parser. If you compile a program named `foo.rs`, its
-top-level module will be called `foo`, and you can set `RUST_LOG` to
-`foo` to enable `warn`, `info` and `debug` logging for the module.
-
-Turned-off `log` statements impose minimal overhead on the code that
-contains them, because the arguments to `log` are evaluated lazily.
-So except in code that needs to be really, really fast,
-you should feel free to scatter around debug logging statements, and
-leave them in.
-
-Three macros that combine text-formatting (as with `fmt!`) and logging
-are available. These take a string and any number of format arguments,
-and will log the formatted string:
-
-~~~~
-# fn get_error_string() -> ~str { ~"boo" }
-warn!("only %d seconds remaining", 10);
-error!("fatal: %s", get_error_string());
-~~~~
-
-Because the macros `debug!`, `warn!`, and `error!` expand to calls to `log`,
-their arguments are also lazily evaluated.
-
 # Functions
 
 Like all other static declarations, such as `type`, functions can be
@@ -921,8 +693,12 @@ with the `fn` keyword, the type of arguments are specified following
 colons and the return type follows the arrow.
 
 ~~~~
-fn int_to_str(i: int) -> ~str {
-    return ~"tube sock";
+fn repeat(string: &str, count: int) -> ~str {
+    let mut result = ~"";
+    for count.times {
+        result += string;
+    }
+    return result;
 }
 ~~~~
 
@@ -967,100 +743,63 @@ In Rust, these are annotated with the pseudo-return type '`!`':
 fn dead_end() -> ! { fail }
 ~~~~
 
-This helps the compiler avoid spurious error messages. For example,
-the following code would be a type error if `dead_end` would be
-expected to return.
-
-~~~~
-# fn can_go_left() -> bool { true }
-# fn can_go_right() -> bool { true }
-# enum dir { left, right }
-# fn dead_end() -> ! { fail; }
-let dir = if can_go_left() { left }
-          else if can_go_right() { right }
-          else { dead_end(); };
-~~~~
+Using `!` in your code instead of making up a return type helps the compiler
+avoid spurious error messages.
 
 # Basic datatypes
 
-The core datatypes of Rust are structural records, enums (tagged
-unions, algebraic data types), and tuples. They are immutable
-by default.
+The core datatypes of Rust are structs, enums (tagged unions, algebraic data
+types), and tuples. They are immutable by default.
 
 ~~~~
-type point = {x: float, y: float};
+struct Point { x: float, y: float }
 
-enum shape {
-    circle(point, float),
-    rectangle(point, point)
+enum Shape {
+    Circle(Point, float),
+    Rectangle(Point, Point)
 }
 ~~~~
 
-## Records
+## Structs
 
-Rust record types are written `{field1: T1, field2: T2 [, ...]}`,
-where `T1`, `T2`, ... denote types.  Record literals are written in
-the same way, but with expressions instead of types. They are quite
-similar to C structs, and even laid out the same way in memory (so you
-can read from a Rust struct in C, and vice-versa). The dot operator is
-used to access record fields (`mypoint.x`).
-
-Fields that you want to mutate must be explicitly marked `mut`.
-
-~~~~
-type stack = {content: ~[int], mut head: uint};
-~~~~
+Rust struct types must be declared before they are used using the `struct`
+syntax: `struct Name { field1: T1, field2: T2 [, ...] }`, where `T1`, `T2`,
+... denote types. To construct a struct, use the same syntax, but leave off
+the `struct`; for example: `Point { x: 1.0, y: 2.0 }`.
 
-With such a type, you can do `mystack.head += 1u`. If `mut` were
-omitted from the type, such an assignment would result in a type
-error.
+Structs are quite similar to C structs and are even laid out the same way in
+memory (so you can read from a Rust struct in C, and vice-versa). The dot
+operator is used to access struct fields (`mypoint.x`).
 
-To create a new record based on the value of an existing record
-you construct it using the `with` keyword:
+Fields that you want to mutate must be explicitly marked `mut`.
 
 ~~~~
-# impl {x:float, y:float} : core::cmp::Eq {
-#   pure fn eq(&&other: {x:float, y:float}) -> bool {
-#       self.x == other.x && self.y == other.y
-#   }
-# }
-
-let oldpoint = {x: 10f, y: 20f};
-let newpoint = {x: 0f with oldpoint};
-assert newpoint == {x: 0f, y: 20f};
+struct Stack {
+    content: ~[int],
+    mut head: uint
+}
 ~~~~
 
-This will create a new record, copying all the fields from `oldpoint`
-into it, except for the ones that are explicitly set in the literal.
-
-Rust record types are *structural*. This means that `{x: float, y:
-float}` is not just a way to define a new type, but is the actual name
-of the type. Record types can be used without first defining them. If
-module A defines `type point = {x: float, y: float}`, and module B,
-without knowing anything about A, defines a function that returns an
-`{x: float, y: float}`, you can use that return value as a `point` in
-module A. (Remember that `type` defines an additional name for a type,
-not an actual new type.)
+With a value of such a type, you can do `mystack.head += 1`. If `mut` were
+omitted from the type, such an assignment would result in a type error.
 
-## Record patterns
-
-Records can be destructured in `match` patterns. The basic syntax is
-`{fieldname: pattern, ...}`, but the pattern for a field can be
-omitted as a shorthand for simply binding the variable with the same
-name as the field.
+## Struct patterns
 
+Structs can be destructured in `match` patterns. The basic syntax is
+`Name {fieldname: pattern, ...}`:
 ~~~~
-# let mypoint = {x: 0f, y: 0f};
+# struct Point { x: float, y: float }
+# let mypoint = Point { x: 0.0, y: 0.0 };
 match mypoint {
-    {x: 0f, y: y_name} => { /* Provide sub-patterns for fields */ }
-    {x, y}             => { /* Simply bind the fields */ }
+    Point { x: 0.0, y: y } => { io::println(y.to_str());                    }
+    Point { x: x, y: y }   => { io::println(x.to_str() + " " + y.to_str()); }
 }
 ~~~~
 
-The field names of a record do not have to appear in a pattern in the
-same order they appear in the type. When you are not interested in all
-the fields of a record, a record pattern may end with `, _` (as in
-`{field1, _}`) to indicate that you're ignoring all other fields.
+In general, the field names of a struct do not have to appear in the same
+order they appear in the type. When you are not interested in all
+the fields of a struct, a struct pattern may end with `, _` (as in
+`Name {field1, _}`) to indicate that you're ignoring all other fields.
 
 ## Enums
 
@@ -1068,15 +807,15 @@ Enums are datatypes that have several alternate representations. For
 example, consider the type shown earlier:
 
 ~~~~
-# type point = {x: float, y: float};
-enum shape {
-    circle(point, float),
-    rectangle(point, point)
+# struct Point { x: float, y: float }
+enum Shape {
+    Circle(Point, float),
+    Rectangle(Point, Point)
 }
 ~~~~
 
-A value of this type is either a circle, in which case it contains a
-point record and a float, or a rectangle, in which case it contains
+A value of this type is either a Circle, in which case it contains a
+point struct and a float, or a Rectangle, in which case it contains
 two point records. The run-time representation of such a value
 includes an identifier of the actual form that it holds, much like the
 'tagged union' pattern in C, but with better ergonomics.