diff options
| author | Brian Anderson <banderson@mozilla.com> | 2012-10-06 22:23:16 -0700 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2012-10-06 22:24:15 -0700 |
| commit | d5d774124730ee363620c6e4ec5bf65deea2385c (patch) | |
| tree | afe2f61bafa74986678ef4d697cca21b7a237268 | |
| parent | f5c95de212d7851c7f8788a328455ed381e38978 (diff) | |
| download | rust-d5d774124730ee363620c6e4ec5bf65deea2385c.tar.gz rust-d5d774124730ee363620c6e4ec5bf65deea2385c.zip | |
Overhaul mods and crates section of tutorial
| -rw-r--r-- | doc/lib/codemirror-rust.js | 2 | ||||
| -rw-r--r-- | doc/tutorial.md | 315 |
2 files changed, 154 insertions, 163 deletions
diff --git a/doc/lib/codemirror-rust.js b/doc/lib/codemirror-rust.js index 02f2c603955..727882d3099 100644 --- a/doc/lib/codemirror-rust.js +++ b/doc/lib/codemirror-rust.js @@ -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"}; diff --git a/doc/tutorial.md b/doc/tutorial.md index 37b0b722eaa..f73ddd1b4c4 100644 --- a/doc/tutorial.md +++ b/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. -## Using other crates +~~~ {.xfail-test} +// foo.rc +#[link(name = "foo", vers="1.0")]; -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]. +mod bar; +~~~ -[std]: http://doc.rust-lang.org/doc/std/index/General.html +~~~ {.xfail-test} +// foo.rs +fn main() { bar::baz(); } +~~~ -`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. +> ***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. -It is possible to provide more specific information when using an -external crate. +> ***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. -~~~~ {.ignore} -extern mod myfarm (name = "farm", vers = "2.7"); -~~~~ +## Using other crates + +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. -When a comma-separated list of name/value pairs is given after `use`, -these are matched against the attributes provided in the `link` +For example, `extern mod std` links the [standard library]. + +[standard library]: std/index.html + +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. - -## The core library +Which can then be linked with any (or all) of the following: -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. +~~~~ {.xfail-test} +extern mod farm; +extern mod my_farm (name = "farm", vers = "2.5"); +extern mod my_auxiliary_farm (name = "farm", author = "mjh"); +~~~~ -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 - -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. - -~~~~ -extern mod std; -use io::println; -fn main() { - println(~"that was easy"); -} -~~~~ - - -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}`). - -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`: +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. -~~~~ -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; - } -} -~~~~ +## The core library -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. +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. -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. +All Rust programs link to the core library and import its contents, +as if the following were written at the top of the crate. -~~~~ -fn main() { - let x = 10; - let x = x + 10; - assert x == 20; -} -~~~~ +~~~ {.xfail-test} +extern mod core; +use core::*; +~~~ -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? |
