1
Fork 0

Overhaul mods and crates section of tutorial

This commit is contained in:
Brian Anderson 2012-10-06 22:23:16 -07:00
parent f5c95de212
commit d5d7741247
2 changed files with 155 additions and 164 deletions

View file

@ -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"};

View file

@ -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?