From df34fe917a6dd8b4696a36bf4e4e3e162ca97607 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 31 Aug 2012 17:20:36 -0700 Subject: [PATCH] doc: Trim down the tutorial a little more --- doc/tutorial.md | 127 +++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 72 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 0fc56f135c95..a0b2dc71f29c 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -303,14 +303,6 @@ The double-colon (`::`) is used as a module separator, so `io::println` means 'the thing named `println` in the module named `io`. -Rust will normally emit warnings about unused variables. These can be -suppressed by using a variable name that starts with an underscore. - -~~~~ -fn this_warns(x: int) {} -fn this_doesnt(_x: int) {} -~~~~ - ## Variable declaration The `let` keyword, as we've seen, introduces a local variable. Local @@ -319,11 +311,11 @@ a local variable that can be reassigned. Global constants can be defined with `const`: ~~~~ -const repeat: int = 5; +const REPEAT: int = 5; fn main() { let hi = "Hi!"; let mut count = 0; - while count < repeat { + while count < REPEAT { io::println(hi); count += 1; } @@ -340,28 +332,6 @@ let my_favorite_value: int = my_favorite_value as int; ## Types -The `-> bool` in the `is_four` example is the way a function's return -type is written. For functions that do not return a meaningful value -(these conceptually return nil in Rust), you can optionally say `-> -()` (`()` is how nil is written), but usually the return annotation is -simply left off, as in the `fn main() { ... }` examples we've seen -earlier. - -Every argument to a function must have its type declared (for example, -`x: int`). Inside the function, type inference will be able to -automatically deduce the type of most locals (generic functions, which -we'll come back to later, will occasionally need additional -annotation). Locals can be written either with or without a type -annotation: - -~~~~ -// The type of this vector will be inferred based on its use. -let x = []; -# vec::map(x, fn&(&&_y:int) -> int { _y }); -// Explicitly say this is a vector of zero integers. -let y: [int * 0] = []; -~~~~ - The basic types are written like this: `()` @@ -407,16 +377,16 @@ more detail later on (the `T`s here stand for any other 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: +Some types can only be manipulated by pointer, never directly. For instance, +you cannot refer to a string (`str`); instead you refer to a pointer to a +string (`@str`, `~str`, or `&str`). These *dynamically-sized* types consist +of: `fn(arg1: T1, arg2: T2) -> T3` : Function types. `str` - : String type. A string contains a UTF-8 encoded sequence of characters. + : String type (in UTF-8). `[T]` : Vector with unknown size (also called a slice). @@ -437,6 +407,28 @@ 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. +## Using types + +The `-> bool` in the `is_four` example is the way a function's return +type is written. For functions that do not return a meaningful value, +you can optionally say `-> ()`, but usually the return annotation is simply +left off, as in the `fn main() { ... }` examples we've seen earlier. + +Every argument to a function must have its type declared (for example, +`x: int`). Inside the function, type inference will be able to +automatically deduce the type of most locals (generic functions, which +we'll come back to later, will occasionally need additional +annotation). Locals can be written either with or without a type +annotation: + +~~~~ +// The type of this vector will be inferred based on its use. +let x = []; +# vec::map(x, fn&(&&_y:int) -> int { _y }); +// Explicitly say this is a vector of zero integers. +let y: [int * 0] = []; +~~~~ + ## Numeric literals Integers can be written in decimal (`144`), hexadecimal (`0x90`), and @@ -478,20 +470,20 @@ 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. As in C, -the bit operators `>>`, `<<`, `&`, `|`, and `^` are supported. +Rust's set of operators contains very few surprises. Arithmetic is done with +`*`, `/`, `%`, `+`, and `-` (multiply, divide, remainder, plus, minus). `-` is +also a unary prefix operator that does negation. As in C, the bit operators +`>>`, `<<`, `&`, `|`, and `^` are also supported. -Note that, if applied an integer value, `!` inverts all the bits. +Note that, if applied to an integer value, `!` flips all the bits (like `~` in +C). The comparison operators are the traditional `==`, `!=`, `<`, `>`, `<=`, and `>=`. Short-circuiting (lazy) boolean operators are written `&&` (and) and `||` (or). -For type casting, Rust uses the binary `as` operator, which has high -precedence, just lower than multiplication and division. It takes an -expression on the left side, and a type on the right side, and will, +For type casting, Rust uses the binary `as` operator. It takes an +expression on the left side and a type on the right side and will, if a meaningful conversion exists, convert the result of the expression to the given type. @@ -508,11 +500,12 @@ more likely to be what you expect (unless you are a C veteran). ## Syntax extensions -*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. +*Syntax extensions* are special forms that are not built into the language, +but are instead provided by the libraries. To make it clear to the reader when +a syntax extension is being used, the names of all syntax extensions end with +`!`. The standard library defines a few syntax extensions, the most useful of +which is `fmt!`, a `sprintf`-style text formatter that is expanded at compile +time. ~~~~ io::println(fmt!("%s is %d", ~"the answer", 42)); @@ -524,7 +517,8 @@ don't match the types of the arguments. [pf]: http://en.cppreference.com/w/cpp/io/c/fprintf -You can define your own syntax extensions via macros. +You can define your own syntax extensions with the macro system, which is out +of scope of this tutorial. # Control structures @@ -581,8 +575,8 @@ construct when it is finished. 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 `..`. The underscore -(`_`) is a wildcard pattern that matches everything. +of numeric literal patterns can be expressed with two dots, as in `M..N`. The +underscore (`_`) is a wildcard pattern that matches everything. The patterns in an match arm are followed by a fat arrow, `=>`, then an expression to evaluate. Each case is separated by commas. It's often @@ -601,10 +595,9 @@ match my_number { } ~~~ -If the arm with the wildcard pattern was left off in the above -example, the typechecker would reject it at compile time. `match` -constructs must be exhaustive: they must have an arm covering every -possible case. +`match` constructs must be *exhaustive*: they must have an arm covering every +possible case. For example, if the arm with the wildcard pattern was left off +in the above example, the typechecker would reject it. A powerful application of pattern matching is *destructuring*, where you use the matching to get at the contents of data types. Remember @@ -632,11 +625,11 @@ an expression of type `bool` that determines, after the pattern is found to match, whether the arm is taken or not. The variables bound by the pattern are available in this guard expression. -## Destructuring let +## Let -To a limited extent, it is possible to use destructuring patterns when -declaring a variable with `let`. For example, you can say this to -extract the fields from a tuple: +You've already seen simple `let` bindings. `let` is also a little fancier: it +is possible to use destructuring patterns in it. For example, you can say this +to extract the fields from a tuple: ~~~~ # fn get_tuple_of_two_ints() -> (int, int) { (1, 1) } @@ -646,7 +639,7 @@ let (a, b) = get_tuple_of_two_ints(); This will introduce two new variables, `a` and `b`, bound to the content of the tuple. -You may only use irrefutable patterns—patterns that can never fail to +You may only use *irrefutable* patterns—patterns that can never fail to match—in let bindings. Other types of patterns, such as literals, are not allowed. @@ -736,16 +729,6 @@ fn do_nothing_the_hard_way() -> () { return (); } fn do_nothing_the_easy_way() { } ~~~~ -Some functions (such as the C function `exit`) never return normally. -In Rust, these are annotated with the pseudo-return type '`!`': - -~~~~ -fn dead_end() -> ! { fail } -~~~~ - -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 structs, enums (tagged unions, algebraic data