Overhaul mods and crates section of tutorial
This commit is contained in:
parent
f5c95de212
commit
d5d7741247
2 changed files with 155 additions and 164 deletions
|
@ -9,7 +9,7 @@ CodeMirror.defineMode("rust", function() {
|
||||||
"as": "op", "true": "atom", "false": "atom", "assert": "op", "check": "op",
|
"as": "op", "true": "atom", "false": "atom", "assert": "op", "check": "op",
|
||||||
"claim": "op", "extern": "ignore", "unsafe": "ignore", "import": "else-style",
|
"claim": "op", "extern": "ignore", "unsafe": "ignore", "import": "else-style",
|
||||||
"export": "else-style", "copy": "op", "log": "op",
|
"export": "else-style", "copy": "op", "log": "op",
|
||||||
"use": "op", "self": "atom"
|
"use": "op", "self": "atom", "pub": "atom", "priv": "atom"
|
||||||
};
|
};
|
||||||
var typeKeywords = function() {
|
var typeKeywords = function() {
|
||||||
var keywords = {"fn": "fn"};
|
var keywords = {"fn": "fn"};
|
||||||
|
|
317
doc/tutorial.md
317
doc/tutorial.md
|
@ -31,7 +31,7 @@ pleasant high-level features include:
|
||||||
|
|
||||||
This is an introductory tutorial for the Rust programming language. It
|
This is an introductory tutorial for the Rust programming language. It
|
||||||
covers the fundamentals of the language, including the syntax, the
|
covers the fundamentals of the language, including the syntax, the
|
||||||
type system and memory model, and generics. [Additional
|
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.
|
depth.
|
||||||
|
|
||||||
|
@ -2113,61 +2113,123 @@ This usage of traits is similar to Java interfaces.
|
||||||
|
|
||||||
# Modules and crates
|
# Modules and crates
|
||||||
|
|
||||||
The Rust namespace is divided into modules. Each source file starts
|
The Rust namespace is arranged in a hierarchy of modules. Each source
|
||||||
with its own module.
|
(.rs) file represents a single module and may in turn contain
|
||||||
|
additional modules.
|
||||||
## Local modules
|
|
||||||
|
|
||||||
The `mod` keyword can be used to open a new, local module. In the
|
|
||||||
example below, `chicken` lives in the module `farm`, so, unless you
|
|
||||||
explicitly import it, you must refer to it by its long name,
|
|
||||||
`farm::chicken`.
|
|
||||||
|
|
||||||
~~~~
|
~~~~
|
||||||
mod farm {
|
mod farm {
|
||||||
pub fn chicken() -> ~str { ~"cluck cluck" }
|
pub fn chicken() -> ~str { ~"cluck cluck" }
|
||||||
pub fn cow() -> ~str { ~"mooo" }
|
pub fn cow() -> ~str { ~"mooo" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
io::println(farm::chicken());
|
io::println(farm::chicken());
|
||||||
}
|
}
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
Modules can be nested to arbitrary depth.
|
The contents of modules can be imported into the current scope
|
||||||
|
with the `use` keyword, optionally giving it an alias. `use`
|
||||||
|
may appear at the beginning of crates, `mod`s, `fn`s, and other
|
||||||
|
blocks.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
# mod farm { pub fn chicken() { } }
|
||||||
|
# fn main() {
|
||||||
|
// Bring `chicken` into scope
|
||||||
|
use farm::chicken;
|
||||||
|
|
||||||
|
fn chicken_farmer() {
|
||||||
|
// The same, but name it `my_chicken`
|
||||||
|
use my_chicken = farm::chicken;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
# }
|
||||||
|
~~~
|
||||||
|
|
||||||
|
These farm animal functions have a new keyword, `pub`, attached to
|
||||||
|
them. This is a visibility modifier that allows item to be accessed
|
||||||
|
outside of the module in which they are declared, using `::`, as in
|
||||||
|
`farm::chicken`. Items, like `fn`, `struct`, etc. are private by
|
||||||
|
default.
|
||||||
|
|
||||||
|
Visibility restrictions in Rust exist only at module boundaries. This
|
||||||
|
is quite different from most object-oriented languages that also enforce
|
||||||
|
restrictions on objects themselves. That's not to say that Rust doesn't
|
||||||
|
support encapsulation - both struct fields and methods can be private -
|
||||||
|
but it is at the module level, not the class level. Note that fields
|
||||||
|
and methods are _public_ by default.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
mod farm {
|
||||||
|
# pub fn make_me_a_farm() -> farm::Farm { farm::Farm { chickens: ~[], cows: ~[], farmer: Human(0) } }
|
||||||
|
pub struct Farm {
|
||||||
|
priv mut chickens: ~[Chicken],
|
||||||
|
priv mut cows: ~[Cow],
|
||||||
|
farmer: Human
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note - visibility modifiers on impls currently have no effect
|
||||||
|
impl Farm {
|
||||||
|
priv fn feed_chickens() { ... }
|
||||||
|
priv fn feed_cows() { ... }
|
||||||
|
fn add_chicken(c: Chicken) { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn feed_animals(farm: &Farm) {
|
||||||
|
farm.feed_chickens();
|
||||||
|
farm.feed_cows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let f = make_me_a_farm();
|
||||||
|
f.add_chicken(make_me_a_chicken());
|
||||||
|
farm::feed_animals(&f);
|
||||||
|
f.farmer.rest();
|
||||||
|
}
|
||||||
|
# type Chicken = int;
|
||||||
|
# type Cow = int;
|
||||||
|
# enum Human = int;
|
||||||
|
# fn make_me_a_farm() -> farm::Farm { farm::make_me_a_farm() }
|
||||||
|
# fn make_me_a_chicken() -> Chicken { 0 }
|
||||||
|
# impl Human { fn rest() { } }
|
||||||
|
~~~
|
||||||
|
|
||||||
## Crates
|
## Crates
|
||||||
|
|
||||||
The unit of independent compilation in Rust is the crate. Libraries
|
The unit of independent compilation in Rust is the crate - rustc
|
||||||
tend to be packaged as crates, and your own programs may consist of
|
compiles a single crate at a time, from which it produces either a
|
||||||
one or more crates.
|
library or executable.
|
||||||
|
|
||||||
When compiling a single `.rs` file, the file acts as the whole crate.
|
When compiling a single `.rs` file, the file acts as the whole crate.
|
||||||
You can compile it with the `--lib` compiler switch to create a shared
|
You can compile it with the `--lib` compiler switch to create a shared
|
||||||
library, or without, provided that your file contains a `fn main`
|
library, or without, provided that your file contains a `fn main`
|
||||||
somewhere, to create an executable.
|
somewhere, to create an executable.
|
||||||
|
|
||||||
It is also possible to include multiple files in a crate. For this
|
Larger crates typically span multiple files and are compiled from
|
||||||
purpose, you create a `.rc` crate file, which references any number of
|
a crate (.rc) file. Crate files contain their own syntax for loading
|
||||||
`.rs` code files. A crate file could look like this:
|
modules from .rs files and typically include metadata about the crate.
|
||||||
|
|
||||||
~~~~ {.ignore}
|
~~~~ { .xfail-test }
|
||||||
#[link(name = "farm", vers = "2.5", author = "mjh")];
|
#[link(name = "farm", vers = "2.5", author = "mjh")];
|
||||||
#[crate_type = "lib"];
|
#[crate_type = "lib"];
|
||||||
|
|
||||||
mod cow;
|
mod cow;
|
||||||
mod chicken;
|
mod chicken;
|
||||||
mod horse;
|
mod horse;
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
Compiling this file will cause `rustc` to look for files named
|
Compiling this file will cause `rustc` to look for files named
|
||||||
`cow.rs`, `chicken.rs`, `horse.rs` in the same directory as the `.rc`
|
`cow.rs`, `chicken.rs`, and `horse.rs` in the same directory as the
|
||||||
file, compile them all together, and, depending on the presence of the
|
`.rc` file, compile them all together, and, based on the presence of
|
||||||
`crate_type = "lib"` attribute, output a shared library or an executable.
|
the `crate_type = "lib"` attribute, output a shared library or an
|
||||||
(If the line `#[crate_type = "lib"];` was omitted, `rustc` would create an
|
executable. (If the line `#[crate_type = "lib"];` was omitted,
|
||||||
executable.)
|
`rustc` would create an executable.)
|
||||||
|
|
||||||
The `#[link(...)]` part provides meta information about the module,
|
The `#[link(...)]` attribute provides meta information about the
|
||||||
which other crates can use to load the right module. More about that
|
module, which other crates can use to load the right module. More
|
||||||
later.
|
about that later.
|
||||||
|
|
||||||
To have a nested directory structure for your source files, you can
|
To have a nested directory structure for your source files, you can
|
||||||
nest mods in your `.rc` file:
|
nest mods in your `.rc` file:
|
||||||
|
@ -2184,56 +2246,65 @@ The compiler will now look for `poultry/chicken.rs` and
|
||||||
and `poultry::turkey`. You can also provide a `poultry.rs` to add
|
and `poultry::turkey`. You can also provide a `poultry.rs` to add
|
||||||
content to the `poultry` module itself.
|
content to the `poultry` module itself.
|
||||||
|
|
||||||
The compiler then builds the crate as a platform-specific shared library or
|
When compiling .rc files, if rustc finds a .rs file with the same
|
||||||
executable which can be distributed.
|
name, then that .rs file provides the top-level content of the crate.
|
||||||
|
|
||||||
|
~~~ {.xfail-test}
|
||||||
|
// foo.rc
|
||||||
|
#[link(name = "foo", vers="1.0")];
|
||||||
|
|
||||||
|
mod bar;
|
||||||
|
~~~
|
||||||
|
|
||||||
|
~~~ {.xfail-test}
|
||||||
|
// foo.rs
|
||||||
|
fn main() { bar::baz(); }
|
||||||
|
~~~
|
||||||
|
|
||||||
|
> ***Note***: The way rustc looks for .rs files to pair with .rc
|
||||||
|
> files is a major source of confusion and will change. It's likely
|
||||||
|
> that the crate and source file grammars will merge.
|
||||||
|
|
||||||
|
> ***Note***: The way that directory modules are handled will also
|
||||||
|
> change. The code for directory modules currently lives in a .rs
|
||||||
|
> file with the same name as the directory, _next to_ the directory.
|
||||||
|
> A new scheme will make that file live _inside_ the directory.
|
||||||
|
|
||||||
## Using other crates
|
## Using other crates
|
||||||
|
|
||||||
Having compiled a crate that contains the `#[crate_type = "lib"]`
|
Having compiled a crate into a library you can use it in another crate
|
||||||
attribute, you can use it in another crate with a `use`
|
with an `extern mod` directive. `extern mod` can appear at the top of
|
||||||
directive. We've already seen `extern mod std` in several of the
|
a crate file or at the top of modules. It will cause the compiler to
|
||||||
examples, which loads in the [standard library][std].
|
look in the library search path (which you can extend with `-L`
|
||||||
|
switch) for a compiled Rust library with the right name, then add a
|
||||||
|
module with that crate's name into the local scope.
|
||||||
|
|
||||||
[std]: http://doc.rust-lang.org/doc/std/index/General.html
|
For example, `extern mod std` links the [standard library].
|
||||||
|
|
||||||
`use` directives can appear in a crate file, or at the top level of a
|
[standard library]: std/index.html
|
||||||
single-file `.rs` crate. They will cause the compiler to search its
|
|
||||||
library search path (which you can extend with `-L` switch) for a Rust
|
|
||||||
crate library with the right name.
|
|
||||||
|
|
||||||
It is possible to provide more specific information when using an
|
When a comma-separated list of name/value pairs is given after `extern
|
||||||
external crate.
|
mod`, these are matched against the attributes provided in the `link`
|
||||||
|
|
||||||
~~~~ {.ignore}
|
|
||||||
extern mod myfarm (name = "farm", vers = "2.7");
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
When a comma-separated list of name/value pairs is given after `use`,
|
|
||||||
these are matched against the attributes provided in the `link`
|
|
||||||
attribute of the crate file, and a crate is only used when the two
|
attribute of the crate file, and a crate is only used when the two
|
||||||
match. A `name` value can be given to override the name used to search
|
match. A `name` value can be given to override the name used to search
|
||||||
for the crate. So the above would import the `farm` crate under the
|
for the crate.
|
||||||
local name `myfarm`.
|
|
||||||
|
|
||||||
Our example crate declared this set of `link` attributes:
|
Our example crate declared this set of `link` attributes:
|
||||||
|
|
||||||
~~~~ {.ignore}
|
~~~~ {.xfail-test}
|
||||||
#[link(name = "farm", vers = "2.5", author = "mjh")];
|
#[link(name = "farm", vers = "2.5", author = "mjh")];
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
The version does not match the one provided in the `use` directive, so
|
Which can then be linked with any (or all) of the following:
|
||||||
unless the compiler can find another crate with the right version
|
|
||||||
somewhere, it will complain that no matching crate was found.
|
|
||||||
|
|
||||||
## The core library
|
~~~~ {.xfail-test}
|
||||||
|
extern mod farm;
|
||||||
|
extern mod my_farm (name = "farm", vers = "2.5");
|
||||||
|
extern mod my_auxiliary_farm (name = "farm", author = "mjh");
|
||||||
|
~~~~
|
||||||
|
|
||||||
A set of basic library routines, mostly related to built-in datatypes
|
If any of the requested metadata does not match then the crate
|
||||||
and the task system, are always implicitly linked and included in any
|
will not be compiled successfully.
|
||||||
Rust program.
|
|
||||||
|
|
||||||
This library is documented [here][core].
|
|
||||||
|
|
||||||
[core]: core/index.html
|
|
||||||
|
|
||||||
## A minimal example
|
## A minimal example
|
||||||
|
|
||||||
|
@ -2246,7 +2317,7 @@ these two files:
|
||||||
fn explore() -> ~str { ~"world" }
|
fn explore() -> ~str { ~"world" }
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
~~~~ {.ignore}
|
~~~~ {.xfail-test}
|
||||||
// main.rs
|
// main.rs
|
||||||
extern mod world;
|
extern mod world;
|
||||||
fn main() { io::println(~"hello " + world::explore()); }
|
fn main() { io::println(~"hello " + world::explore()); }
|
||||||
|
@ -2261,113 +2332,33 @@ Now compile and run like this (adjust to your platform if necessary):
|
||||||
"hello world"
|
"hello world"
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
## Importing
|
Notice that the library produced contains the version in the filename
|
||||||
|
as well as an inscrutable string of alphanumerics. These are both
|
||||||
|
part of Rust's library versioning scheme. The alphanumerics are
|
||||||
|
a hash representing the crate metadata.
|
||||||
|
|
||||||
When using identifiers from other modules, it can get tiresome to
|
## The core library
|
||||||
qualify them with the full module path every time (especially when
|
|
||||||
that path is several modules deep). Rust allows you to import
|
|
||||||
identifiers at the top of a file, module, or block.
|
|
||||||
|
|
||||||
~~~~
|
The Rust [core] library acts as the language runtime and contains
|
||||||
extern mod std;
|
required memory management and task scheduling code as well as a
|
||||||
use io::println;
|
number of modules necessary for effective usage of the primitive
|
||||||
fn main() {
|
types. Methods on [vectors] and [strings], implementations of most
|
||||||
println(~"that was easy");
|
comparison and math operators, and pervasive types like [`Option`]
|
||||||
}
|
and [`Result`] live in core.
|
||||||
~~~~
|
|
||||||
|
|
||||||
|
All Rust programs link to the core library and import its contents,
|
||||||
|
as if the following were written at the top of the crate.
|
||||||
|
|
||||||
It is also possible to import just the name of a module (`use
|
~~~ {.xfail-test}
|
||||||
std::list;`, then use `list::find`), to import all identifiers exported
|
extern mod core;
|
||||||
by a given module (`use io::*`), or to import a specific set
|
use core::*;
|
||||||
of identifiers (`use math::{min, max, pi}`).
|
~~~
|
||||||
|
|
||||||
Rust uses different namespaces for modules, types, and values. You
|
[core]: core/index.html
|
||||||
can also rename an identifier when importing using the `=` operator:
|
[vectors]: core/vec.html
|
||||||
|
[strings]: core/str.html
|
||||||
~~~~
|
[`Option`]: core/option.html
|
||||||
use prnt = io::println;
|
[`Result`]: core/result.html
|
||||||
~~~~
|
|
||||||
|
|
||||||
## Exporting
|
|
||||||
|
|
||||||
By default, a module exports everything that it defines. This can be
|
|
||||||
restricted with `export` directives at the top of the module or file.
|
|
||||||
|
|
||||||
~~~~
|
|
||||||
mod enc {
|
|
||||||
export encrypt, decrypt;
|
|
||||||
const SUPER_SECRET_NUMBER: int = 10;
|
|
||||||
fn encrypt(n: int) -> int { n + SUPER_SECRET_NUMBER }
|
|
||||||
fn decrypt(n: int) -> int { n - SUPER_SECRET_NUMBER }
|
|
||||||
}
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
This defines a rock-solid encryption algorithm. Code outside of the
|
|
||||||
module can refer to the `enc::encrypt` and `enc::decrypt` identifiers
|
|
||||||
just fine, but it does not have access to `enc::super_secret_number`.
|
|
||||||
|
|
||||||
## Resolution
|
|
||||||
|
|
||||||
The resolution process in Rust simply goes up the chain of contexts,
|
|
||||||
looking for the name in each context. Nested functions and modules
|
|
||||||
create new contexts inside their parent function or module. A file
|
|
||||||
that's part of a bigger crate will have that crate's context as its
|
|
||||||
parent context.
|
|
||||||
|
|
||||||
Identifiers can shadow each other. In this program, `x` is of type
|
|
||||||
`int`:
|
|
||||||
|
|
||||||
~~~~
|
|
||||||
type MyType = ~str;
|
|
||||||
fn main() {
|
|
||||||
type MyType = int;
|
|
||||||
let x: MyType = 17;
|
|
||||||
}
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
An `use` directive will only import into the namespaces for which
|
|
||||||
identifiers are actually found. Consider this example:
|
|
||||||
|
|
||||||
~~~~
|
|
||||||
mod foo {
|
|
||||||
fn bar() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let bar = 10;
|
|
||||||
|
|
||||||
{
|
|
||||||
use foo::bar;
|
|
||||||
let quux = bar;
|
|
||||||
assert quux == 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
When resolving the type name `bar` in the `quux` definition, the
|
|
||||||
resolver will first look at local block context for `baz`. This has an
|
|
||||||
import named `bar`, but that's function, not a value, So it continues
|
|
||||||
to the `baz` function context and finds a value named `bar` defined
|
|
||||||
there.
|
|
||||||
|
|
||||||
Normally, multiple definitions of the same identifier in a scope are
|
|
||||||
disallowed. Local variables defined with `let` are an exception to
|
|
||||||
this—multiple `let` directives can redefine the same variable in a
|
|
||||||
single scope. When resolving the name of such a variable, the most
|
|
||||||
recent definition is used.
|
|
||||||
|
|
||||||
~~~~
|
|
||||||
fn main() {
|
|
||||||
let x = 10;
|
|
||||||
let x = x + 10;
|
|
||||||
assert x == 20;
|
|
||||||
}
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
This makes it possible to rebind a variable without actually mutating
|
|
||||||
it, which is mostly useful for destructuring (which can rebind, but
|
|
||||||
not assign).
|
|
||||||
|
|
||||||
# What next?
|
# What next?
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue