|
1 | 1 | # Modules and crates
|
2 | 2 |
|
3 |
| -FIXME include name resolution details |
| 3 | +The Rust namespace is divided in modules. Each source file starts with |
| 4 | +its own, empty module. |
| 5 | + |
| 6 | +## Local modules |
| 7 | + |
| 8 | +The `mod` keyword can be used to open a new, local module. In the |
| 9 | +example below, `chicken` lives in the module `farm`, so, unless you |
| 10 | +explicitly import it, you must refer to it by its long name, |
| 11 | +`farm::chicken`. |
| 12 | + |
| 13 | + mod farm { |
| 14 | + fn chicken() -> str { "cluck cluck" } |
| 15 | + fn cow() -> str { "mooo" } |
| 16 | + } |
| 17 | + fn main() { |
| 18 | + log_err farm::chicken(); |
| 19 | + } |
| 20 | + |
| 21 | +Modules can be nested to arbitrary depth. |
| 22 | + |
| 23 | +## Crates |
| 24 | + |
| 25 | +The unit of independent compilation in Rust is the crate. Libraries |
| 26 | +tend to be packaged as crates, and your own programs may consist of |
| 27 | +one or more crates. |
| 28 | + |
| 29 | +When compiling a single `.rs` file, the file acts as the whole crate. |
| 30 | +You can compile it with the `--lib` compiler switch to create a shared |
| 31 | +library, or without, provided that your file contains a `fn main` |
| 32 | +somewhere, to create an executable. |
| 33 | + |
| 34 | +It is also possible to include multiple files in a crate. For this |
| 35 | +purpose, you create a `.rc` crate file, which references any number of |
| 36 | +`.rs` code files. A crate file could look like this: |
| 37 | + |
| 38 | + #[link(name = "farm", vers = "2.5", author = "mjh")] |
| 39 | + mod cow; |
| 40 | + mod chicken; |
| 41 | + mod horse; |
| 42 | + |
| 43 | +Compiling this file will cause `rustc` to look for files named |
| 44 | +`cow.rs`, `chicken.rs`, `horse.rs` in the same directory as the `.rc` |
| 45 | +file, compile them all together, and, depending on the presence of the |
| 46 | +`--lib` switch, output a shared library or an executable. |
| 47 | + |
| 48 | +The `#[link(...)]` part provides meta information about the module, |
| 49 | +which other crates can use to load the right module. More about that |
| 50 | +in a moment. |
| 51 | + |
| 52 | +To have a nested directory structure for your source files, you can |
| 53 | +nest mods in your `.rc` file: |
| 54 | + |
| 55 | + mod poultry { |
| 56 | + mod chicken; |
| 57 | + mod turkey; |
| 58 | + } |
| 59 | + |
| 60 | +The compiler will now look for `poultry/chicken.rs` and |
| 61 | +`poultry/turkey.rs`, and export their content in `poultry::chicken` |
| 62 | +and `poultry::turkey`. You can also provide a `poultry.rs` to add |
| 63 | +content to the `poultry` module itself. |
| 64 | + |
| 65 | +## Using other crates |
| 66 | + |
| 67 | +Having compiled a crate with `--lib`, you can use it in another crate |
| 68 | +with a `use` directive. We've already seen `use std` in several of the |
| 69 | +examples, which loads in the standard library. |
| 70 | + |
| 71 | +`use` directives can appear in a crate file, or at the top level of a |
| 72 | +single-file `.rs` crate. They will cause the compiler to search its |
| 73 | +library search path (which you can extend with `-L` switch) for a Rust |
| 74 | +crate library with the right name. This name is deduced from the crate |
| 75 | +name in a platform-dependent way. The `farm` library will be called |
| 76 | +`farm.dll` on Windows, `libfarm.so` on Linux, and `libfarm.dylib` on |
| 77 | +OS X. |
| 78 | + |
| 79 | +It is possible to provide more specific information when using an |
| 80 | +external crate. |
| 81 | + |
| 82 | + use myfarm (name = "farm", vers = "2.7"); |
| 83 | + |
| 84 | +When a comma-separated list of name/value pairs is given after `use`, |
| 85 | +these are matched against the attributes provided in the `link` |
| 86 | +attribute of the crate file, and a crate is only used when the two |
| 87 | +match. A `name` value can be given to override the name used to search |
| 88 | +for the crate. So the above would import the `farm` crate under the |
| 89 | +local name `myfarm`. |
| 90 | + |
| 91 | +Our example crate declared this set of `link` attributes: |
| 92 | + |
| 93 | + #[link(name = "farm", vers = "2.5", author = "mjh")] |
| 94 | + |
| 95 | +The version does not match the one provided in the `use` directive, so |
| 96 | +unless the compiler can find another crate with the right version |
| 97 | +somewhere, it will complain that no matching crate was found. |
| 98 | + |
| 99 | +## A minimal example |
| 100 | + |
| 101 | +Now for something that you can actually compile yourself. We have |
| 102 | +these two files: |
| 103 | + |
| 104 | + // mylib.rs |
| 105 | + fn world() -> str { "world" } |
| 106 | + |
| 107 | + // main.rs |
| 108 | + use mylib; |
| 109 | + fn main() { log_err "hello " + mylib::world(); } |
| 110 | + |
| 111 | +FIXME the compiler currently complains about missing link metas when you compile this |
| 112 | + |
| 113 | +Now compile and run like this (adjust to your platform if necessary): |
| 114 | + |
| 115 | + > rustc --lib mylib.rs |
| 116 | + > rustc main.rs -L . |
| 117 | + > ./main |
| 118 | + "hello world" |
| 119 | + |
| 120 | +## Importing |
| 121 | + |
| 122 | +When using identifiers from other modules, it can get tiresome to |
| 123 | +qualify them with the full module path every time (especially when |
| 124 | +that path is several modules deep). Rust allows you to import |
| 125 | +identifiers at the top of a file or module. |
| 126 | + |
| 127 | + use std; |
| 128 | + import std::io::println; |
| 129 | + fn main() { |
| 130 | + println("that was easy"); |
| 131 | + } |
| 132 | + |
| 133 | +It is also possible to import just the name of a module (`import |
| 134 | +std::io;`, then use `io::println`), import all identifiers exported by |
| 135 | +a given module (`import std::io::*`), or to import a specific set of |
| 136 | +identifiers (`import std::math::{min, max, pi}`). |
| 137 | + |
| 138 | +It is also possible to rename an identifier when importing, using the |
| 139 | +`=` operator: |
| 140 | + |
| 141 | + import prnt = std::io::println; |
| 142 | + |
| 143 | +## Exporting |
| 144 | + |
| 145 | +By default, a module exports everything that it defines. This can be |
| 146 | +restricted with `export` directives at the top of the module or file. |
| 147 | + |
| 148 | + mod enc { |
| 149 | + export encrypt, decrypt; |
| 150 | + const super_secret_number: int = 10; |
| 151 | + fn encrypt(n: int) { n + super_secret_number } |
| 152 | + fn decrypt(n: int) { n - super_secret_number } |
| 153 | + } |
| 154 | + |
| 155 | +This defines a rock-solid encryption algorithm. Code outside of the |
| 156 | +module can refer to the `enc::encrypt` and `enc::decrypt` identifiers |
| 157 | +just fine, but it does not have access to `enc::syper_secret_number`. |
| 158 | + |
| 159 | +## Namespaces |
| 160 | + |
| 161 | +Rust uses three different namespaces. One for modules, one for types, |
| 162 | +and one for values. This means that this code is valid: |
| 163 | + |
| 164 | + mod buffalo { |
| 165 | + type buffalo = int; |
| 166 | + fn buffalo(buffalo: buffalo) -> buffalo { buffalo } |
| 167 | + } |
| 168 | + fn main() { |
| 169 | + let buffalo: buffalo::buffalo = 1; |
| 170 | + buffalo::buffalo(buffalo::buffalo(buffalo)); |
| 171 | + } |
| 172 | + |
| 173 | +You don't want to write things like that, but it *is* very practical |
| 174 | +to not have to worry about name clashes between types, values, and |
| 175 | +modules. This allows us to have a module `std::str`, for example, even |
| 176 | +though `str` is a built-in type name. |
| 177 | + |
| 178 | +## Resolution |
| 179 | + |
| 180 | +The resolution process in Rust simply goes up the chain of contexts, |
| 181 | +looking for the name in each context. Nested functions and modules |
| 182 | +create new contexts inside their parent function or module. A file |
| 183 | +that's part of a bigger crate will have that crate's context as parent |
| 184 | +context. |
| 185 | + |
| 186 | +Identifiers can shadow each others. In this program, `x` is of type |
| 187 | +`int`: |
| 188 | + |
| 189 | + type x = str; |
| 190 | + fn main() { |
| 191 | + type x = int; |
| 192 | + let x: int; |
| 193 | + } |
| 194 | + |
| 195 | +An `import` directive will only import into the namespaces for which |
| 196 | +identifiers are actually found. Consider this example: |
| 197 | + |
| 198 | + type bar = uint; |
| 199 | + mod foo { fn bar() {} } |
| 200 | + mod baz { |
| 201 | + import foo::bar; |
| 202 | + const x: bar = 20u; |
| 203 | + } |
| 204 | + |
| 205 | +When resolving the type name `bar` in the `const` definition, the |
| 206 | +resolver will first look at the module context for `baz`. This has an |
| 207 | +import named `bar`, but that's a function, not a type, So it continues |
| 208 | +to the top level and finds a type named `bar` defined there. |
| 209 | + |
| 210 | +Normally, multiple definitions of the same identifier in a scope are |
| 211 | +disallowed. Local variables defined with `let` are an exception to |
| 212 | +this—multiple `let` directives can redefine the same variable in a |
| 213 | +single scope. When resolving the name of such a variable, the most |
| 214 | +recent definition is used. |
| 215 | + |
| 216 | + fn main() { |
| 217 | + let x = 10; |
| 218 | + let x = x + 10; |
| 219 | + assert x == 20; |
| 220 | + } |
| 221 | + |
| 222 | +This makes it possible to rebind a variable without actually mutating |
| 223 | +it, which is mostly useful for destructuring (which can rebind, but |
| 224 | +not assign). |
0 commit comments