doc: Update the tutorial

This commit is contained in:
Patrick Walton 2012-08-31 16:57:37 -07:00
parent 329281ebcc
commit 9eaaceb9f0

View file

@ -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):
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:
~~~~
# fn a_function(_a: ()) {}
a_function(while false {});
# 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.
This also works for function bodies. This function returns a boolean:
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 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.
`f32`, `f64`
: Floating-point types with a specific size.
`char`
: A character is a 32-bit Unicode code point.
`~str`
: String type. A string contains a UTF-8 encoded sequence of characters.
: 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.
`fn(arg1: T1, arg2: T2) -> T3`, `fn@()`, `fn~()`, `fn&()`
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`
: 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:
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:
~~~~
let x = 3;
fn identity_u16(n: u16) -> u16 { n }
identity_u16(x);
let x = 50;
log(error, x); // x is an int
let y = 100u;
log(error, y); // y is an uint
~~~~
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 }
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 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`).
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 }`.
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`).
Fields that you want to mutate must be explicitly marked `mut`.
~~~~
type stack = {content: ~[int], mut head: uint};
~~~~
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.
To create a new record based on the value of an existing record
you construct it using the `with` keyword:
~~~~
# 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};
~~~~
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.)
## 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.
~~~~
# let mypoint = {x: 0f, y: 0f};
match mypoint {
{x: 0f, y: y_name} => { /* Provide sub-patterns for fields */ }
{x, y} => { /* Simply bind the fields */ }
struct Stack {
content: ~[int],
mut head: uint
}
~~~~
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.
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.
## Struct patterns
Structs can be destructured in `match` patterns. The basic syntax is
`Name {fieldname: pattern, ...}`:
~~~~
# struct Point { x: float, y: float }
# let mypoint = Point { x: 0.0, y: 0.0 };
match mypoint {
Point { x: 0.0, y: y } => { io::println(y.to_str()); }
Point { x: x, y: y } => { io::println(x.to_str() + " " + y.to_str()); }
}
~~~~
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.