There are some intertwined concepts in Rust module system:
- Visibility
- Physical file path
- Logical module path
One should note that these three concepts are completely independent to others.
While rustc automatically infers the physical file path from the logical module path,
it is just for the convenience and you can always override this behavior with #[path]
attribute.
I'll give some practical examples to illustrate the differences among these three concepts.
// a.rs
fn answer() -> uint { 42 }
fn main() {
println!("{}", answer());
}
This is the simplest module structure possible in Rust.
There is exactly one module (unnamed), which is also a root module for the current crate.
The root module can have crate attributes for the current crate;
it can be, for example, used to explicitly set the crate name to something other than a
(automatically inferred from the file name).
The module can have multiple items (functions, statics, uses and other modules).
Items have visibilities (pub
lic or priv
ate) attached to them.
The visibilities are ignored when accessing items from the same module, though,
so you won't need any visibility specifications in this file.
// a.rs
mod universe {
pub fn answer() -> uint { 42 }
}
fn main() {
println!("{}", universe::answer());
}
This time there are two modules, an unnamed root module and a module named universe
.
The universe
module is defined in the root module, so the root module does not need visibility for referring universe
.
But universe::answer
function is not defined in the root module, so it needs pub
for referring universe::answer
.
The position of module items is not quite important. It can be important for macros (which only obeys the basic nesting and source code order), so you need to be a bit more careful when you are using macros within modules.
// a.rs
use universe::answer;
mod universe {
pub fn answer() -> uint { 42 }
}
fn main() {
println!("{}", answer());
}
This is same as above but uses an use
item to import the visible item into the root module.
When you have use a::b::c;
, you need accesses to a
, a::b
and a::b::c
.
In this case, the root module defined universe
so it can see that, and universe::answer
is visible from outside.
The order of use
and mod
is again not important.
It seemingly violates the use-after-definition principle, but it's how Rust currently does that.
// life_universe_and_everything.rs
pub fn answer() -> uint { 42 }
// a.rs
use universe::answer;
#[path="life_universe_and_everything.rs"] mod universe;
fn main() {
println!("{}", answer());
}
This is same as above, but universe
no longer has its contents (note the semicolon after mod universe
).
Instead, its contents is in the separate file pointed by #[path]
attribute.
Everything else didn't change; the file is just a convenient way to organize a large crate with many modules inside.
In fact, even for the macros, mod foo;
will just act like mod foo { <contents of foo.rs inside> }
.
// universe.rs
pub fn answer() -> uint { 42 }
// a.rs
use universe::answer;
mod universe;
fn main() {
println!("{}", answer());
}
This is pretty much same as above but there is no #[path]
attribute.
Rustc will assume that the physical file name for that module is same as the module name plus .rs
extension.
Sometimes, you need to organize modules a bit further...
// universe/mod.rs
pub fn answer() -> uint { 42 }
// a.rs
use universe::answer;
mod universe;
fn main() {
println!("{}", answer());
}
...like this. Rustc will try module_name.rs
first, and if it does not exist, will try module_name/mod.rs
instead.
(Rustc will helpfully report an error when both exist.)
TODO