diff options
| author | Brian Anderson <banderson@mozilla.com> | 2012-07-01 19:20:43 -0700 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2012-07-02 00:14:25 -0700 |
| commit | b446ea8710f83d8677e7e3569a02055e84ffbc76 (patch) | |
| tree | a38956f3418b9a03413e7445d15e4eb7aa334a12 | |
| parent | 129de960236bb71bd55b744d3ee2c03539e0bb7c (diff) | |
| download | rust-b446ea8710f83d8677e7e3569a02055e84ffbc76.tar.gz rust-b446ea8710f83d8677e7e3569a02055e84ffbc76.zip | |
tutorial: Some work on closures
| -rw-r--r-- | doc/lib/codemirror-rust.js | 2 | ||||
| -rw-r--r-- | doc/tutorial.md | 98 |
2 files changed, 63 insertions, 37 deletions
diff --git a/doc/lib/codemirror-rust.js b/doc/lib/codemirror-rust.js index 6a1ec766453..900180c5eb5 100644 --- a/doc/lib/codemirror-rust.js +++ b/doc/lib/codemirror-rust.js @@ -1,7 +1,7 @@ CodeMirror.defineMode("rust", function() { var indentUnit = 4, altIndentUnit = 2; var valKeywords = { - "if": "if-style", "while": "if-style", "else": "else-style", + "if": "if-style", "while": "if-style", "loop": "if-style", "else": "else-style", "do": "else-style", "ret": "else-style", "fail": "else-style", "break": "atom", "cont": "atom", "const": "let", "resource": "fn", "let": "let", "fn": "fn", "for": "for", "alt": "alt", "iface": "iface", diff --git a/doc/tutorial.md b/doc/tutorial.md index fe08fa95d12..8096f67cafc 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -75,7 +75,7 @@ builds require that: * You can at least execute snapshot binaries of one of the forms we offer them in. Currently we build and test snapshots on: * Windows (7, server 2008 r2) x86 only - * Linux 2.6.x (various distributions) x86 and x86-64 + * Linux (various distributions) x86 and x86-64 * OSX 10.6 ("Snow leopard") or 10.7 ("Lion") x86 and x86-64 You may find other platforms work, but these are our "tier 1" supported @@ -97,9 +97,9 @@ Windows requires some extra steps: please see the [getting started][wiki-get-started] page on the Rust wiki. ~~~~ {.notrust} -$ wget http://dl.rust-lang.org/dist/rust-0.1.tar.gz -$ tar -xzf rust-0.1.tar.gz -$ cd rust-0.1 +$ wget http://dl.rust-lang.org/dist/rust-0.3.tar.gz +$ tar -xzf rust-0.3.tar.gz +$ cd rust-0.3 $ ./configure $ make && make install ~~~~ @@ -535,16 +535,12 @@ information that can serve a variety of purposes. One of those is conditional compilation: ~~~~ -#[cfg(target_os = "win32")] +#[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 (it -allows `cfg(flag=value)` and `cfg(flag)` forms, where the second -simply checks whether the configuration flag is defined at all). Flags -for `target_os` and `target_arch` are set by the compiler. It is -possible to set additional flags with the `--cfg` command-line option. +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 @@ -554,8 +550,8 @@ 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 the `cfg` example -above, or in this [crate](#modules-and-crates) metadata declaration: +comma-separated list of nested attributes, as in this +[crate](#modules-and-crates) metadata declaration: ~~~~ {.ignore} #[link(name = "std", @@ -593,6 +589,7 @@ compile-time. ~~~~ io::println(#env("PATH")); ~~~~ + # Control structures ## Conditionals @@ -723,7 +720,7 @@ while cake_amount > 0 { ~~~~ let mut x = 5; -while true { +loop { x += x - 3; if x % 5 == 0 { break; } io::println(int::str(x)); @@ -795,7 +792,9 @@ their arguments are also lazily evaluated. 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. +*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; @@ -805,7 +804,7 @@ assert x == 10; # Functions -Functions (like all other static declarations, such as `type`) can be +Like all other static declarations, such as `type`, functions can be declared both at the top level and inside other functions (or modules, which we'll come back to in moment). @@ -838,34 +837,60 @@ let dir = if can_go_left() { left } ## Closures -Named functions, like those in the previous section, do not close over -their environment. Rust also includes support for closures, which are -functions that can access variables in the scope in which they are -created. +Named functions, like those in the previous section, may not refer +to local variables decalared outside the function - they do not +close over their environment. For example you couldn't write the +following: + +~~~~ {.ignore} +let foo = 10; + +fn bar() -> int { + ret foo; // `bar` cannot refer to `foo` +} +~~~~ -There are several forms of closures, each with its own role. The most -common type is called a 'stack closure'; this is a closure which has -full access to its environment. +Rust also supports _closures_, functions that can +access variables in the enclosing scope. ~~~~ fn call_closure_with_ten(b: fn(int)) { b(10); } -let x = 20; -call_closure_with_ten(|arg| #info("x=%d, arg=%d", x, arg) ); +let x = 20; +let closure = |arg| #info("x=%d, arg=%d", x, arg); + +call_closure_with_ten(closure); +~~~~ + +A closure is defined by listing the arguments, between bars, followed +by an expression that acts as the function body. The types of the +arguments are generally omitted, as is the return type, because +the compiler can almost always infer them. In the rare case where +the compiler needs assistance though, the arguments and return +types may be annotated. + +~~~~ +# type mygoodness = fn(str) -> str; type what_the = int; +let bloop = |well, oh: mygoodness| -> what_the { fail oh(well) }; +~~~~ + +There are several forms of closure, each with its own role. The most +common, called a _stack closure_ and written `&fn()` has direct access +to local variables in the enclosing scope. + +~~~~ +let mut max = 0; +[1, 2, 3].map(|x| if x > max { max = x }); ~~~~ -This defines a function that accepts a closure, and then calls it with -a simple stack closure that executes a log statement, accessing both -its argument and the variable `x` from its environment. +Stack closures are very efficient because their environment is +allocated on the call stack and refers by pointer to captured +locals. To ensure that stack closures never outlive the local +variables to which they refer, they can only be used in argument +position and cannot be stored in structures nor returned from +functions. Despite the usage limitations stack closures are used +pervasively in Rust code. -Stack closures are called stack closures because they directly access -the stack frame in which they are created. This makes them very -lightweight to construct and lets them modify local variables from the -enclosing scope, but it also makes it unsafe for the closure to -survive the scope in which it was created. To prevent them from being -used after the creating scope has returned, stack closures can only be -used in a restricted way: they are allowed to appear in function -argument position and in call position, but nowhere else. ### Boxed closures @@ -908,7 +933,8 @@ that callers have the flexibility to pass whatever they want. ~~~~ fn call_twice(f: fn()) { f(); f(); } -call_twice(|| { "I am a stack closure"; } ); +call_twice(|| { "I am an inferred stack closure"; } ); +call_twice(fn&() { "I am also a stack closure"; } ); call_twice(fn@() { "I am a boxed closure"; }); fn bare_function() { "I am a plain function"; } call_twice(bare_function); |
