X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fdoc%2Ftutorial.md;h=6abfa21962fac1035d5638f13819bfdb493b27a7;hb=028159ead403c09efcaa2b0bb0738d33209c3950;hp=179e67328da828a83a366ebb27dc9ddb61c8154f;hpb=f49572373347b4110e7140fafbcb57db112712bd;p=rust.git diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 179e67328da..6abfa21962f 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -33,7 +33,7 @@ pleasant high-level features include: This is an introductory tutorial for the Rust programming language. It covers the fundamentals of the language, including the syntax, the type system and memory model, generics, and modules. [Additional -tutorials](#what-next) cover specific language features in greater +tutorials](#what-next?) cover specific language features in greater depth. This tutorial assumes that the reader is already familiar with one or @@ -982,7 +982,8 @@ The obvious approach is to define `Cons` as containing an element in the list along with the next `List` node. However, this will generate a compiler error. ~~~ {.ignore} -// error: illegal recursive enum type; wrap the inner value in a box to make it representable +// error: illegal recursive enum type; wrap the inner value in a box to make it +// representable enum List { Cons(u32, List), // an element (`u32`) and the next node in the list Nil @@ -1054,10 +1055,10 @@ immutable, the whole list is immutable. The memory allocation itself is the box, while the owner holds onto a pointer to it: ~~~ {.notrust} - List box List box List box List box - +--------------+ +--------------+ +--------------+ +--------------+ -list -> | Cons | 1 | ~ | -> | Cons | 2 | ~ | -> | Cons | 3 | ~ | -> | Nil | - +--------------+ +--------------+ +--------------+ +--------------+ + List box List box List box List box + +--------------+ +--------------+ +--------------+ +----------+ +list -> | Cons | 1 | ~ | -> | Cons | 2 | ~ | -> | Cons | 3 | ~ | -> | Nil | + +--------------+ +--------------+ +--------------+ +----------+ ~~~ > *Note:* the above diagram shows the logical contents of the enum. The actual @@ -1196,8 +1197,9 @@ fn eq(xs: &List, ys: &List) -> bool { match (xs, ys) { // If we have reached the end of both lists, they are equal. (&Nil, &Nil) => true, - // If the current element in both lists is equal, keep going. - (&Cons(x, ~ref next_xs), &Cons(y, ~ref next_ys)) if x == y => eq(next_xs, next_ys), + // If the current elements of both lists are equal, keep going. + (&Cons(x, ~ref next_xs), &Cons(y, ~ref next_ys)) + if x == y => eq(next_xs, next_ys), // If the current elements are not equal, the lists are not equal. _ => false } @@ -1256,7 +1258,7 @@ Using the generic `List` works much like before, thanks to type inference: # Cons(value, ~xs) # } let mut xs = Nil; // Unknown type! This is a `List`, but `T` can be anything. -xs = prepend(xs, 10); // The compiler infers the type of `xs` as `List` from this. +xs = prepend(xs, 10); // Here the compiler infers `xs`'s type as `List`. xs = prepend(xs, 15); xs = prepend(xs, 20); ~~~ @@ -1302,8 +1304,9 @@ fn eq(xs: &List, ys: &List) -> bool { match (xs, ys) { // If we have reached the end of both lists, they are equal. (&Nil, &Nil) => true, - // If the current element in both lists is equal, keep going. - (&Cons(ref x, ~ref next_xs), &Cons(ref y, ~ref next_ys)) if x == y => eq(next_xs, next_ys), + // If the current elements of both lists are equal, keep going. + (&Cons(ref x, ~ref next_xs), &Cons(ref y, ~ref next_ys)) + if x == y => eq(next_xs, next_ys), // If the current elements are not equal, the lists are not equal. _ => false } @@ -1330,8 +1333,9 @@ impl Eq for List { match (self, ys) { // If we have reached the end of both lists, they are equal. (&Nil, &Nil) => true, - // If the current element in both lists is equal, keep going. - (&Cons(ref x, ~ref next_xs), &Cons(ref y, ~ref next_ys)) if x == y => next_xs == next_ys, + // If the current elements of both lists are equal, keep going. + (&Cons(ref x, ~ref next_xs), &Cons(ref y, ~ref next_ys)) + if x == y => next_xs == next_ys, // If the current elements are not equal, the lists are not equal. _ => false } @@ -1716,38 +1720,103 @@ environment (sometimes referred to as "capturing" variables in their environment). For example, you couldn't write the following: ~~~~ {.ignore} -let foo = 10; +let x = 3; -fn bar() -> int { - return foo; // `bar` cannot refer to `foo` -} +// `fun` cannot refer to `x` +fn fun() -> () { println!("{}", x); } ~~~~ -Rust also supports _closures_, functions that can access variables in -the enclosing scope. +A _closure_ does support accessing the enclosing scope; below we will create +2 _closures_ (nameless functions). Compare how `||` replaces `()` and how +they try to access `x`: -~~~~ -fn call_closure_with_ten(b: |int|) { b(10); } +~~~~ {.ignore} +let x = 3; -let captured_var = 20; -let closure = |arg| println!("captured_var={}, arg={}", captured_var, arg); +// `fun` is an invalid definition +fn fun () -> () { println!("{}", x) } // cannot capture from enclosing scope +let closure = || -> () { println!("{}", x) }; // can capture from enclosing scope -call_closure_with_ten(closure); +// `fun_arg` is an invalid definition +fn fun_arg (arg: int) -> () { println!("{}", arg + x) } // cannot capture +let closure_arg = |arg: int| -> () { println!("{}", arg + x) }; // can capture +// ^ +// Requires a type because the implementation needs to know which `+` to use. +// In the future, the implementation may not need the help. + +fun(); // Still won't work +closure(); // Prints: 3 + +fun_arg(7); // Still won't work +closure_arg(7); // Prints: 10 ~~~~ Closures begin with the argument list between vertical bars and are followed by a single expression. Remember that a block, `{ ; ; ... }`, is considered a single expression: it evaluates to the result of the last expression it contains if that expression is not followed by a semicolon, -otherwise the block evaluates to `()`. +otherwise the block evaluates to `()`, the unit value. + +In general, return types and all argument types must be specified +explicitly for function definitions. (As previously mentioned in the +[Functions section](#functions), omitting the return type from a +function declaration is synonymous with an explicit declaration of +return type unit, `()`.) -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. +~~~~ {.ignore} +fn fun (x: int) { println!("{}", x) } // this is same as saying `-> ()` +fn square(x: int) -> uint { (x * x) as uint } // other return types are explicit +// Error: mismatched types: expected `()` but found `uint` +fn badfun(x: int) { (x * x) as uint } ~~~~ -let square = |x: int| -> uint { (x * x) as uint }; + +On the other hand, the compiler can usually infer both the argument +and return types for a closure expression; therefore they are often +omitted, since both a human reader and the compiler can deduce the +types from the immediate context. This is in contrast to function +declarations, which require types to be specified and are not subject +to type inference. Compare: + +~~~~ {.ignore} +// `fun` as a function declaration cannot infer the type of `x`, so it must be provided +fn fun (x: int) { println!("{}", x) } +let closure = |x | { println!("{}", x) }; // infers `x: int`, return type `()` + +// For closures, omitting a return type is *not* synonymous with `-> ()` +let add_3 = |y | { 3i + y }; // infers `y: int`, return type `int`. + +fun(10); // Prints 10 +closure(20); // Prints 20 +closure(add_3(30)); // Prints 33 + +fun("String"); // Error: mismatched types + +// Error: mismatched types +// inference already assigned `closure` the type `|int| -> ()` +closure("String"); +~~~~ + +In cases where the compiler needs assistance, the arguments and return +types may be annotated on closures, using the same notation as shown +earlier. In the example below, since different types provide an +implementation for the operator `*`, the argument type for the `x` +parameter must be explicitly provided. + +~~~~{.ignore} +// Error: the type of `x` must be known to be used with `x * x` +let square = |x | -> uint { (x * x) as uint }; +~~~~ + +In the corrected version, the argument type is explicitly annotated, +while the return type can still be inferred. + +~~~~ +let square_explicit = |x: int| -> uint { (x * x) as uint }; +let square_infer = |x: int| { (x * x) as uint }; + +println!("{}", square_explicit(20)); // 400 +println!("{}", square_infer(-20)); // 400 ~~~~ There are several forms of closure, each with its own role. The most @@ -1793,11 +1862,6 @@ spawn(proc() { }); ~~~~ -> *Note:* If you want to see the output of `debug!` statements, you will need to turn on -> `debug!` logging. To enable `debug!` logging, set the RUST_LOG environment -> variable to the name of your crate, which, for a file named `foo.rs`, will be -> `foo` (e.g., with bash, `export RUST_LOG=foo`). - ## Closure compatibility Rust closures have a convenient subtyping property: you can pass any kind of @@ -2966,7 +3030,7 @@ use farm::*; ~~~ > *Note:* This feature of the compiler is currently gated behind the -> `#[feature(globs)]` directive. More about these directives can be found in +> `#![feature(globs)]` directive. More about these directives can be found in > the manual. However, that's not all. You can also rename an item while you're bringing it into scope: @@ -2981,7 +3045,7 @@ fn main() { } ~~~ -In general, `use` creates an local alias: +In general, `use` creates a local alias: An alternate path and a possibly different name to access the same item, without touching the original, and with both being interchangeable.