The tutorial is undergoing a complete re-write as
the Guide.
Until it's ready, this tutorial is the right place to come to start learning
Rust. Please submit improvements as pull requests, but understand that
eventually it will be going away.
# Introduction
Rust is a programming language with a focus on type safety, memory
safety, concurrency and performance. It is intended for writing
large-scale, high-performance software that is free from several
classes of common errors. Rust has a sophisticated memory model that
encourages efficient data structures and safe concurrency patterns,
forbidding invalid memory accesses that would otherwise cause
segmentation faults. It is statically typed and compiled ahead of
time.
As a multi-paradigm language, Rust supports writing code in
procedural, functional and object-oriented styles. Some of its
pleasant high-level features include:
* **Type inference.** Type annotations on local variable declarations
are optional.
* **Safe task-based concurrency.** Rust's lightweight tasks do not share
memory, instead communicating through messages.
* **Higher-order functions.** Efficient and flexible closures provide
iteration and other control structures
* **Pattern matching and algebraic data types.** Pattern matching on
Rust's enumeration types (a more powerful version of C's enums,
similar to algebraic data types in functional languages) is a
compact and expressive way to encode program logic.
* **Polymorphism.** Rust has type-parametric functions and
types, type classes and OO-style interfaces.
## Scope
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
depth.
This tutorial assumes that the reader is already familiar with one or
more languages in the C family. Understanding of pointers and general
memory management techniques will help.
## Conventions
Throughout the tutorial, language keywords and identifiers defined in
example code are displayed in `code font`.
Code snippets are indented, and also shown in a monospaced font. Not
all snippets constitute whole programs. For brevity, we'll often show
fragments of programs that don't compile on their own. To try them
out, you might have to wrap them in `fn main() { ... }`, and make sure
they don't contain references to names that aren't actually defined.
> *Warning:* Rust is a language under ongoing development. Notes
> about potential changes to the language, implementation
> deficiencies, and other caveats appear offset in blockquotes.
# Getting started
There are two ways to install the Rust compiler: by building from source or
by downloading prebuilt binaries or installers for your platform. The
[install page][rust-install] contains links to download binaries for both
the nightly build and the most current Rust major release. For Windows and
OS X, the install page provides links to native installers.
> *Note:* Windows users should read the detailed
> [Getting started][wiki-start] notes on the wiki. Even when using
> the binary installer, the Windows build requires a MinGW installation,
> the precise details of which are not discussed here.
For Linux and OS X, the install page provides links to binary tarballs.
To install the Rust compiler from the from a binary tarball, download
the binary package, extract it, and execute the `install.sh` script in
the root directory of the package.
To build the Rust compiler from source, you will need to obtain the source through
[Git][git] or by downloading the source package from the [install page][rust-install].
Since the Rust compiler is written in Rust, it must be built by
a precompiled "snapshot" version of itself (made in an earlier state
of development). The source build automatically fetches these snapshots
from the Internet on our supported platforms.
Snapshot binaries are currently built and tested on several platforms:
* Windows (7, 8, Server 2008 R2), x86 only
* Linux (2.6.18 or later, various distributions), x86 and x86-64
* OSX 10.7 (Lion) or greater, x86 and x86-64
You may find that other platforms work, but these are our "tier 1"
supported build environments that are most likely to work.
[wiki-start]: https://github.com/rust-lang/rust/wiki/Note-getting-started-developing-Rust
[git]: https://github.com/rust-lang/rust.git
[rust-install]: http://www.rust-lang.org/install.html
To build from source you will also need the following prerequisite
packages:
* g++ 4.7 or clang++ 3.x
* python 2.6 or later (but not 3.x)
* perl 5.0 or later
* gnu make 3.81 or later
* curl
If you've fulfilled those prerequisites, something along these lines
should work.
~~~~console
$ curl -O http://static.rust-lang.org/dist/rust-nightly.tar.gz
$ tar -xzf rust-nightly.tar.gz
$ cd rust-nightly
$ ./configure
$ make && make install
~~~~
You may need to use `sudo make install` if you do not normally have
permission to modify the destination directory. The install locations
can be adjusted by passing a `--prefix` argument to
`configure`. Various other options are also supported: pass `--help`
for more information on them.
When complete, `make install` will place several programs into
`/usr/local/bin`: `rustc`, the Rust compiler, and `rustdoc`, the
API-documentation tool.
[tarball]: http://static.rust-lang.org/dist/rust-nightly.tar.gz
[win-exe]: http://static.rust-lang.org/dist/rust-nightly-install.exe
## Compiling your first program
Rust program files are, by convention, given the extension `.rs`. Say
we have a file `hello.rs` containing this program:
~~~~
fn main() {
println!("hello?");
}
~~~~
> *Note:* An identifier followed by an exclamation point, like
> `println!`, is a macro invocation. Macros are explained
> [later](#syntax-extensions); for now just remember to include the
> exclamation point.
If the Rust compiler was installed successfully, running `rustc
hello.rs` will produce an executable called `hello` (or `hello.exe` on
Windows) which, upon running, will likely do exactly what you expect.
The Rust compiler tries to provide useful information when it encounters an
error. If you introduce an error into the program (for example, by changing
`println!` to some nonexistent macro), and then compile it, you'll see
an error message like this:
~~~~text
hello.rs:2:5: 2:24 error: macro undefined: 'print_with_unicorns'
hello.rs:2 print_with_unicorns!("hello?");
^~~~~~~~~~~~~~~~~~~
~~~~
In its simplest form, a Rust program is a `.rs` file with some types
and functions defined in it. If it has a `main` function, it can be
compiled to an executable. Rust does not allow code that's not a
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, even ones not written in Rust.
## Editing Rust code
There are vim highlighting and indentation scripts in the Rust source
distribution under `src/etc/vim/`. There is an emacs mode under
`src/etc/emacs/` called `rust-mode`, but do read the instructions
included in that directory. In particular, if you are running emacs
24, then using emacs's internal package manager to install `rust-mode`
is the easiest way to keep it up to date. There is also a package for
Sublime Text 2, available both [standalone][sublime] and through
[Sublime Package Control][sublime-pkg], and support for Kate
under `src/etc/kate`.
A community-maintained list of available Rust tooling is [on the
wiki][wiki-packages].
There is ctags support via `src/etc/ctags.rust`, but many other
tools and editors are not yet supported. If you end up writing a Rust
mode for your favorite editor, let us know so that we can link to it.
[sublime]: http://github.com/jhasse/sublime-rust
[sublime-pkg]: http://wbond.net/sublime_packages/package_control
# Syntax basics
Assuming you've programmed in any C-family language (C++, Java,
JavaScript, C#, or PHP), Rust will feel familiar. Code is arranged
in blocks delineated by curly braces; there are control structures
for branching and looping, like the familiar `if` and `while`; function
calls are written `myfunc(arg1, arg2)`; operators are written the same
and mostly have the same precedence as in C; comments are again like C;
module names are separated with double-colon (`::`) as with C++.
The main surface difference to be aware of is that the condition at
the head of control structures like `if` and `while` does not require
parentheses, while their bodies *must* be wrapped in
braces. Single-statement, unbraced bodies are not allowed.
~~~~
# mod universe { pub fn recalibrate() -> bool { true } }
fn main() {
/* A simple loop */
loop {
// A tricky calculation
if universe::recalibrate() {
return;
}
}
}
~~~~
The `let` keyword introduces a local variable. Variables are immutable by
default. To introduce a local variable that you can re-assign later, use `let
mut` instead.
~~~~
let hi = "hi";
let mut count = 0i;
while count < 10 {
println!("count is {}", count);
count += 1;
}
~~~~
Although Rust can almost always infer the types of local variables, you can
specify a variable's type by following it in the `let` with a colon, then the
type name. Static items, on the other hand, always require a type annotation.
~~~~
static MONSTER_FACTOR: f64 = 57.8;
let monster_size = MONSTER_FACTOR * 10.0;
let monster_size: int = 50;
~~~~
Local variables may shadow earlier declarations, as in the previous example:
`monster_size` was first declared as a `f64`, and then a second
`monster_size` was declared as an `int`. If you were to actually compile this
example, though, the compiler would determine that the first `monster_size` is
unused and issue a warning (because this situation is likely to indicate a
programmer error). For occasions where unused variables are intentional, their
names may be prefixed with an underscore to silence the warning, like `let
_monster_size = 50;`.
Rust identifiers 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
write function, variable, and module names with lowercase letters, using
underscores where they help readability, while writing types in camel case.
~~~
let my_variable = 100i;
type MyType = int; // primitive types are _not_ camel case
~~~
## Expressions and semicolons
Though it isn't apparent in all code, there is a fundamental
difference between Rust's syntax and predecessors like C.
Many constructs that are statements in C are expressions
in Rust, allowing code to be more concise. For example, you might
write a piece of code like this:
~~~~
# let item = "salad";
let price: f64;
if item == "salad" {
price = 3.50;
} else if item == "muffin" {
price = 2.25;
} else {
price = 2.00;
}
~~~~
But, in Rust, you don't have to repeat the name `price`:
~~~~
# let item = "salad";
let price: f64 =
if item == "salad" {
3.50
} else if item == "muffin" {
2.25
} else {
2.00
};
~~~~
Both pieces of code are exactly equivalent: they assign a value to
`price` depending on the condition that holds. Note that there
are no semicolons in the blocks of 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 `()` (unit or void) to `price`. But without the semicolon, each
branch has a different value, and `price` gets the value of the branch that
was taken.
In short, everything that's not a declaration (declarations are `let` for
variables; `fn` for functions; and any top-level named items such as
[traits](#traits), [enum types](#enums), and static items) is an
expression, including function bodies.
~~~~
fn is_four(x: int) -> bool {
// No need for a return statement. The result of the expression
// is used as the return value.
x == 4
}
~~~~
## Primitive types and literals
There are general signed and unsigned integer types, `int` and `uint`,
as well as 8-, 16-, 32-, and 64-bit variants, `i8`, `u16`, etc.
Integers can be written in decimal (`144`), hexadecimal (`0x90`), octal (`0o70`), or
binary (`0b10010000`) base. Each integral type has a corresponding literal
suffix that can be used to indicate the type of a literal: `i` for `int`,
`u` for `uint`, `i8` for the `i8` type.
In the absence of an integer literal suffix, Rust will infer the
integer type based on type annotations and function signatures in the
surrounding program. In the absence of any type information at all,
Rust will report an error and request that the type be specified explicitly.
~~~~
let a: int = 1; // `a` is an `int`
let b = 10i; // `b` is an `int`, due to the `i` suffix
let c = 100u; // `c` is a `uint`
let d = 1000i32; // `d` is an `i32`
~~~~
There are two floating-point types: `f32`, and `f64`.
Floating-point numbers are written `0.0`, `1e6`, or `2.1e-4`.
Like integers, floating-point literals are inferred to the correct type.
Suffixes `f32`, and `f64` can be used to create literals of a specific type.
The keywords `true` and `false` produce literals of type `bool`.
Characters, the `char` type, are four-byte Unicode codepoints,
whose literals are written between single quotes, as in `'x'`.
Just like C, Rust understands a number of character escapes, using the backslash
character, such as `\n`, `\r`, and `\t`. String literals,
written between double quotes, allow the same escape sequences, and do no
other processing, unlike languages such as PHP or shell.
On the other hand, raw string literals do not process any escape sequences.
They are written as `r##"blah"##`, with a matching number of zero or more `#`
before the opening and after the closing quote, and can contain any sequence of
characters except their closing delimiter. More on strings
[later](#vectors-and-strings).
The unit type, written `()`, has a single value, also written `()`.
## Operators
Rust's set of operators contains very few surprises. Arithmetic is done with
`*`, `/`, `%`, `+`, and `-` (multiply, quotient, remainder, add, and subtract). `-` is
also a unary prefix operator that negates numbers. As in C, the bitwise operators
`>>`, `<<`, `&`, `|`, and `^` are also supported.
Note that, if applied to an integer value, `!` flips all the bits (bitwise
NOT, like `~` in C).
The comparison operators are the traditional `==`, `!=`, `<`, `>`,
`<=`, and `>=`. Short-circuiting (lazy) boolean operators are written
`&&` (and) and `||` (or).
For compile-time 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. Generally, `as` is only used with the primitive numeric types or
pointers, and is not overloadable. [`transmute`][transmute] can be used for
unsafe C-like casting of same-sized types.
~~~~
let x: f64 = 4.0;
let y: uint = x as uint;
assert!(y == 4u);
~~~~
[transmute]: http://doc.rust-lang.org/std/mem/fn.transmute.html
## Syntax extensions
*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 name refers to a syntax extension, the names of all syntax extensions end
with `!`. The standard library defines a few syntax extensions, the most
useful of which is [`format!`][fmt], a `sprintf`-like text formatter that you
will often see in examples, and its related family of macros: `print!`,
`println!`, and `write!`.
`format!` draws syntax from Python, but contains many of the same principles
that [printf][pf] has. Unlike printf, `format!` will give you a compile-time
error when the types of the directives don't match the types of the arguments.
~~~
// `{}` will print the "default format" of a type
println!("{} is {}", "the answer", 43i);
~~~
~~~~
extern crate debug;
# fn main() {
# let mystery_object = ();
// `{:?}` will conveniently print any type,
// but requires the `debug` crate to be linked in
println!("what is this thing: {:?}", mystery_object);
# }
~~~~
[pf]: http://en.cppreference.com/w/cpp/io/c/fprintf
[fmt]: http://doc.rust-lang.org/std/fmt/
You can define your own syntax extensions with the macro system. For details,
see the [macro tutorial][macros]. Note that macro definition is currently
considered an unstable feature.
# Control structures
## Conditionals
We've seen `if` expressions a few times already. To recap, braces are
compulsory, an `if` can have an optional `else` clause, and multiple
`if`/`else` constructs can be chained together:
~~~~
if false {
println!("that's odd");
} else if true {
println!("right");
} else {
println!("neither true nor false");
}
~~~~
The condition given to an `if` construct *must* be of type `bool` (no
implicit conversion happens). If the arms are blocks that have a
value, this value must be of the same type for every arm in which
control reaches the end of the block:
~~~~
fn signum(x: int) -> int {
if x < 0 { -1 }
else if x > 0 { 1 }
else { 0 }
}
~~~~
## 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 labeled with a pattern, and the code compares the value
against each pattern in order until one matches. The matching pattern
executes its corresponding arm.
~~~~
let my_number = 1i;
match my_number {
0 => println!("zero"),
1 | 2 => println!("one or two"),
3..10 => println!("three to ten"),
_ => println!("something else")
}
~~~~
Unlike in C, there is no "falling through" between arms: only one arm
executes, and it doesn't have to explicitly `break` out of the
construct when it is finished.
A `match` arm consists of a *pattern*, then a fat arrow `=>`, followed
by an *action* (expression). Each case is separated by commas. It is
often convenient to use a block expression for each case, in which case
the commas are optional as shown below. Literals are valid patterns and
match only their own value. A single arm may match multiple different
patterns by combining them with the pipe operator (`|`), so long as
every pattern binds the same set of variables (see "destructuring"
below). Ranges of numeric literal patterns can be expressed with two
dots, as in `M..N`. The underscore (`_`) is a wildcard pattern that
matches any single value. (`..`) is a different wildcard that can match
one or more fields in an `enum` variant.
~~~
# let my_number = 1i;
match my_number {
0 => { println!("zero") }
_ => { println!("something else") }
}
~~~
`match` constructs must be *exhaustive*: they must have an arm
covering every possible case. For example, the typechecker would
reject the previous example if the arm with the wildcard pattern was
omitted.
A powerful application of pattern matching is *destructuring*:
matching in order to bind names to the contents of data types.
> *Note:* The following code makes use of tuples (`(f64, f64)`) which
> are explained in section 5.3. For now you can think of tuples as a list of
> items.
~~~~
use std::f64;
fn angle(vector: (f64, f64)) -> f64 {
let pi = f64::consts::PI;
match vector {
(0.0, y) if y < 0.0 => 1.5 * pi,
(0.0, _) => 0.5 * pi,
(x, y) => (y / x).atan()
}
}
~~~~
A variable name in a pattern matches any value, *and* binds that name
to the value of the matched value inside of the arm's action. Thus, `(0.0,
y)` matches any tuple whose first element is zero, and binds `y` to
the second element. `(x, y)` matches any two-element tuple, and binds both
elements to variables. `(0.0,_)` matches any tuple whose first element is zero
and does not bind anything to the second element.
A subpattern can also be bound to a variable, using `variable @ pattern`. For
example:
~~~~
# let age = 23i;
match age {
a @ 0..20 => println!("{} years old", a),
_ => println!("older than 21")
}
~~~~
Any `match` arm can have a guard clause (written `if EXPR`), called a
*pattern guard*, which is 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 in scope in this
guard expression. The first arm in the `angle` example shows an
example of a pattern guard.
You've already seen simple `let` bindings, but `let` is a little
fancier than you've been led to believe. It, too, supports destructuring
patterns. For example, you can write this to extract the fields from a
tuple, introducing two variables at once: `a` and `b`.
~~~~
# fn get_tuple_of_two_ints() -> (int, int) { (1, 1) }
let (a, b) = get_tuple_of_two_ints();
~~~~
Let bindings only work with _irrefutable_ patterns: that is, patterns that can
never fail to match. This excludes `let` from matching literals and most `enum`
variants as binding patterns, since most such patterns are not irrefutable. For
example, this will not compile:
~~~~{ignore}
let (a, 2) = (1, 2);
~~~~
## Loops
`while` denotes a loop that iterates as long as its given condition
(which must have type `bool`) evaluates to `true`. Inside a loop, the
keyword `break` aborts the loop, and `continue` aborts the current
iteration and continues with the next.
~~~~
let mut cake_amount = 8i;
while cake_amount > 0 {
cake_amount -= 1;
}
~~~~
`loop` denotes an infinite loop, and is the preferred way of writing `while true`:
~~~~
let mut x = 5u;
loop {
x += x - 3;
if x % 5 == 0 { break; }
println!("{}", x);
}
~~~~
This code prints out a weird sequence of numbers and stops as soon as
it finds one that can be divided by five.
There is also a for-loop that can be used to iterate over a range of numbers:
~~~~
for n in range(0u, 5) {
println!("{}", n);
}
~~~~
The snippet above prints integer numbers under 5 starting at 0.
More generally, a for loop works with anything implementing the `Iterator` trait.
Data structures can provide one or more methods that return iterators over
their contents. For example, strings support iteration over their contents in
various ways:
~~~~
let s = "Hello";
for c in s.chars() {
println!("{}", c);
}
~~~~
The snippet above prints the characters in "Hello" vertically, adding a new
line after each character.
# Data structures
## Structs
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). Use the dot
operator to access struct fields, as in `mypoint.x`.
~~~~
struct Point {
x: f64,
y: f64
}
~~~~
Structs have "inherited mutability", which means that any field of a struct
may be mutable, if the struct is in a mutable slot.
With a value (say, `mypoint`) of such a type in a mutable location, you can do
`mypoint.y += 1.0`. But in an immutable location, such an assignment to a
struct without inherited mutability would result in a type error.
~~~~ {.ignore}
# struct Point { x: f64, y: f64 }
let mut mypoint = Point { x: 1.0, y: 1.0 };
let origin = Point { x: 0.0, y: 0.0 };
mypoint.y += 1.0; // `mypoint` is mutable, and its fields as well
origin.y += 1.0; // ERROR: assigning to immutable field
~~~~
`match` patterns destructure structs. The basic syntax is
`Name { fieldname: pattern, ... }`:
~~~~
# struct Point { x: f64, y: f64 }
# let mypoint = Point { x: 0.0, y: 0.0 };
match mypoint {
Point { x: 0.0, y: yy } => println!("{}", yy),
Point { x: xx, y: yy } => println!("{} {}", xx, yy)
}
~~~~
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.
Additionally, struct fields have a shorthand matching form that simply
reuses the field name as the binding name.
~~~
# struct Point { x: f64, y: f64 }
# let mypoint = Point { x: 0.0, y: 0.0 };
match mypoint {
Point { x, .. } => println!("{}", x)
}
~~~
## Enums
Enums are datatypes with several alternate representations. A simple `enum`
defines one or more constants, all of which have the same type:
~~~~
enum Direction {
North,
East,
South,
West
}
~~~~
Each variant of this enum has a unique and constant integral discriminator
value. If no explicit discriminator is specified for a variant, the value
defaults to the value of the previous variant plus one. If the first variant
does not have a discriminator, it defaults to 0. For example, the value of
`North` is 0, `East` is 1, `South` is 2, and `West` is 3.
When an enum has simple integer discriminators, you can apply the `as` cast
operator to convert a variant to its discriminator value as an `int`:
~~~~
# enum Direction { North, East, South, West }
println!( "North => {}", North as int );
~~~~
It is possible to set the discriminator values to chosen constant values:
~~~~
enum Color {
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff
}
~~~~
Variants do not have to be simple values; they may be more complex:
~~~~
# struct Point { x: f64, y: f64 }
enum Shape {
Circle(Point, f64),
Rectangle(Point, Point)
}
~~~~
A value of this type is either a `Circle`, in which case it contains a
`Point` struct and a f64, or a `Rectangle`, in which case it contains
two `Point` structs. 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 static guarantees.
This declaration defines a type `Shape` that can refer to such shapes, and two
functions, `Circle` and `Rectangle`, which can be used to construct values of
the type.
To create a new `Circle`, write:
~~~~
# struct Point { x: f64, y: f64 }
# enum Shape { Circle(Point, f64), Rectangle(Point, Point) }
let circle = Circle(Point { x: 0.0, y: 0.0 }, 10.0);
~~~~
All of these variant constructors may be used as patterns. The only way to
access the contents of an enum instance is the destructuring of a match. For
example:
~~~~
use std::f64;
# struct Point {x: f64, y: f64}
# enum Shape { Circle(Point, f64), Rectangle(Point, Point) }
fn area(sh: Shape) -> f64 {
match sh {
Circle(_, size) => f64::consts::PI * size * size,
Rectangle(Point { x, y }, Point { x: x2, y: y2 }) => (x2 - x) * (y2 - y)
}
}
let rect = Rectangle(Point { x: 0.0, y: 0.0 }, Point { x: 2.0, y: 2.0 });
println!("area: {}", area(rect));
~~~~
Use a lone `_` to ignore an individual field. Ignore all fields of a variant
like: `Circle(..)`. Nullary enum patterns are written without parentheses:
~~~~
# struct Point { x: f64, y: f64 }
# enum Direction { North, East, South, West }
fn point_from_direction(dir: Direction) -> Point {
match dir {
North => Point { x: 0.0, y: 1.0 },
East => Point { x: 1.0, y: 0.0 },
South => Point { x: 0.0, y: -1.0 },
West => Point { x: -1.0, y: 0.0 }
}
}
~~~~
Enum variants may also be structs. For example:
~~~~
#![feature(struct_variant)]
use std::f64;
# struct Point { x: f64, y: f64 }
# fn square(x: f64) -> f64 { x * x }
enum Shape {
Circle { center: Point, radius: f64 },
Rectangle { top_left: Point, bottom_right: Point }
}
fn area(sh: Shape) -> f64 {
match sh {
Circle { radius: radius, .. } => f64::consts::PI * square(radius),
Rectangle { top_left: top_left, bottom_right: bottom_right } => {
(bottom_right.x - top_left.x) * (top_left.y - bottom_right.y)
}
}
}
fn main() {
let rect = Rectangle {
top_left: Point { x: 0.0, y: 0.0 },
bottom_right: Point { x: 2.0, y: -2.0 }
};
println!("area: {}", area(rect));
}
~~~~
> *Note:* This feature of the compiler is currently gated behind the
> `#[feature(struct_variant)]` directive. More about these directives can be
> found in the manual.
## Tuples
Tuples in Rust behave exactly like structs, except that their fields do not
have names. Thus, you cannot access their fields with dot notation. Tuples
can have any arity (number of elements) except for 0 (though you may consider
unit, `()`, as the empty tuple if you like).
~~~~
let mytup: (int, int, f64) = (10, 20, 30.0);
match mytup {
(a, b, c) => println!("{}", a + b + (c as int))
}
~~~~
## Tuple structs
Rust also has _tuple structs_, which behave like both structs and tuples,
except that, unlike tuples, tuple structs have names (so `Foo(1, 2)` has a
different type from `Bar(1, 2)`), and tuple structs' _fields_ do not have
names.
For example:
~~~~
struct MyTup(int, int, f64);
let mytup: MyTup = MyTup(10, 20, 30.0);
match mytup {
MyTup(a, b, c) => println!("{}", a + b + (c as int))
}
~~~~