Overhaul mods and crates section of tutorial
This commit is contained in:
parent
f5c95de212
commit
d5d7741247
|
@ -9,7 +9,7 @@ CodeMirror.defineMode("rust", function() {
|
|||
"as": "op", "true": "atom", "false": "atom", "assert": "op", "check": "op",
|
||||
"claim": "op", "extern": "ignore", "unsafe": "ignore", "import": "else-style",
|
||||
"export": "else-style", "copy": "op", "log": "op",
|
||||
"use": "op", "self": "atom"
|
||||
"use": "op", "self": "atom", "pub": "atom", "priv": "atom"
|
||||
};
|
||||
var typeKeywords = function() {
|
||||
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
|
||||
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
|
||||
depth.
|
||||
|
||||
|
@ -2113,61 +2113,123 @@ This usage of traits is similar to Java interfaces.
|
|||
|
||||
# Modules and crates
|
||||
|
||||
The Rust namespace is divided into modules. Each source file starts
|
||||
with its own module.
|
||||
|
||||
## 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`.
|
||||
The Rust namespace is arranged in a hierarchy of modules. Each source
|
||||
(.rs) file represents a single module and may in turn contain
|
||||
additional modules.
|
||||
|
||||
~~~~
|
||||
mod farm {
|
||||
pub fn chicken() -> ~str { ~"cluck cluck" }
|
||||
pub fn cow() -> ~str { ~"mooo" }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
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
|
||||
|
||||
The unit of independent compilation in Rust is the crate. Libraries
|
||||
tend to be packaged as crates, and your own programs may consist of
|
||||
one or more crates.
|
||||
The unit of independent compilation in Rust is the crate - rustc
|
||||
compiles a single crate at a time, from which it produces either a
|
||||
library or executable.
|
||||
|
||||
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
|
||||
library, or without, provided that your file contains a `fn main`
|
||||
somewhere, to create an executable.
|
||||
|
||||
It is also possible to include multiple files in a crate. For this
|
||||
purpose, you create a `.rc` crate file, which references any number of
|
||||
`.rs` code files. A crate file could look like this:
|
||||
Larger crates typically span multiple files and are compiled from
|
||||
a crate (.rc) file. Crate files contain their own syntax for loading
|
||||
modules from .rs files and typically include metadata about the crate.
|
||||
|
||||
~~~~ {.ignore}
|
||||
~~~~ { .xfail-test }
|
||||
#[link(name = "farm", vers = "2.5", author = "mjh")];
|
||||
#[crate_type = "lib"];
|
||||
|
||||
mod cow;
|
||||
mod chicken;
|
||||
mod horse;
|
||||
~~~~
|
||||
|
||||
Compiling this file will cause `rustc` to look for files named
|
||||
`cow.rs`, `chicken.rs`, `horse.rs` in the same directory as the `.rc`
|
||||
file, compile them all together, and, depending on the presence of the
|
||||
`crate_type = "lib"` attribute, output a shared library or an executable.
|
||||
(If the line `#[crate_type = "lib"];` was omitted, `rustc` would create an
|
||||
executable.)
|
||||
`cow.rs`, `chicken.rs`, and `horse.rs` in the same directory as the
|
||||
`.rc` file, compile them all together, and, based on the presence of
|
||||
the `crate_type = "lib"` attribute, output a shared library or an
|
||||
executable. (If the line `#[crate_type = "lib"];` was omitted,
|
||||
`rustc` would create an executable.)
|
||||
|
||||
The `#[link(...)]` part provides meta information about the module,
|
||||
which other crates can use to load the right module. More about that
|
||||
later.
|
||||
The `#[link(...)]` attribute provides meta information about the
|
||||
module, which other crates can use to load the right module. More
|
||||
about that later.
|
||||
|
||||
To have a nested directory structure for your source files, you can
|
||||
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
|
||||
content to the `poultry` module itself.
|
||||
|
||||
The compiler then builds the crate as a platform-specific shared library or
|
||||
executable which can be distributed.
|
||||
When compiling .rc files, if rustc finds a .rs file with the same
|
||||
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
|
||||
|
||||
Having compiled a crate that contains the `#[crate_type = "lib"]`
|
||||
attribute, you can use it in another crate with a `use`
|
||||
directive. We've already seen `extern mod std` in several of the
|
||||
examples, which loads in the [standard library][std].
|
||||
Having compiled a crate into a library you can use it in another crate
|
||||
with an `extern mod` directive. `extern mod` can appear at the top of
|
||||
a crate file or at the top of modules. It will cause the compiler to
|
||||
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
|
||||
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.
|
||||
[standard library]: std/index.html
|
||||
|
||||
It is possible to provide more specific information when using an
|
||||
external crate.
|
||||
|
||||
~~~~ {.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`
|
||||
When a comma-separated list of name/value pairs is given after `extern
|
||||
mod`, these are matched against the attributes provided in the `link`
|
||||
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
|
||||
for the crate. So the above would import the `farm` crate under the
|
||||
local name `myfarm`.
|
||||
for the crate.
|
||||
|
||||
Our example crate declared this set of `link` attributes:
|
||||
|
||||
~~~~ {.ignore}
|
||||
~~~~ {.xfail-test}
|
||||
#[link(name = "farm", vers = "2.5", author = "mjh")];
|
||||
~~~~
|
||||
|
||||
The version does not match the one provided in the `use` directive, so
|
||||
unless the compiler can find another crate with the right version
|
||||
somewhere, it will complain that no matching crate was found.
|
||||
Which can then be linked with any (or all) of the following:
|
||||
|
||||
## 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
|
||||
and the task system, are always implicitly linked and included in any
|
||||
Rust program.
|
||||
|
||||
This library is documented [here][core].
|
||||
|
||||
[core]: core/index.html
|
||||
If any of the requested metadata does not match then the crate
|
||||
will not be compiled successfully.
|
||||
|
||||
## A minimal example
|
||||
|
||||
|
@ -2246,7 +2317,7 @@ these two files:
|
|||
fn explore() -> ~str { ~"world" }
|
||||
~~~~
|
||||
|
||||
~~~~ {.ignore}
|
||||
~~~~ {.xfail-test}
|
||||
// main.rs
|
||||
extern mod world;
|
||||
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"
|
||||
~~~~
|
||||
|
||||
## 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
|
||||
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 core library
|
||||
|
||||
~~~~
|
||||
extern mod std;
|
||||
use io::println;
|
||||
fn main() {
|
||||
println(~"that was easy");
|
||||
}
|
||||
~~~~
|
||||
The Rust [core] library acts as the language runtime and contains
|
||||
required memory management and task scheduling code as well as a
|
||||
number of modules necessary for effective usage of the primitive
|
||||
types. Methods on [vectors] and [strings], implementations of most
|
||||
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
|
||||
std::list;`, then use `list::find`), to import all identifiers exported
|
||||
by a given module (`use io::*`), or to import a specific set
|
||||
of identifiers (`use math::{min, max, pi}`).
|
||||
~~~ {.xfail-test}
|
||||
extern mod core;
|
||||
use core::*;
|
||||
~~~
|
||||
|
||||
Rust uses different namespaces for modules, types, and values. You
|
||||
can also rename an identifier when importing using the `=` operator:
|
||||
|
||||
~~~~
|
||||
use prnt = io::println;
|
||||
~~~~
|
||||
|
||||
## 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).
|
||||
[core]: core/index.html
|
||||
[vectors]: core/vec.html
|
||||
[strings]: core/str.html
|
||||
[`Option`]: core/option.html
|
||||
[`Result`]: core/result.html
|
||||
|
||||
# What next?
|
||||
|
||||
|
|
Loading…
Reference in New Issue