Modules
We have seen how impl blocks let us namespace functions to a type.
Similarly, mod lets us namespace types and functions:
mod foo {
pub fn do_something() {
println!("In the foo module");
}
}
mod bar {
pub fn do_something() {
println!("In the bar module");
}
}
fn main() {
foo::do_something();
bar::do_something();
}
In the foo module
In the bar module
Notes
- Packages provide functionality and include a Cargo.toml file that describes how to build a bundle of 1+ crates.
- Crates are a tree of modules, where a binary crate creates an executable and a library crate compiles to a library.
- Modules define organization, scope, and are the focus of this section.
Visibility
Modules are a privacy boundary:
- Module items are private by default (hides implementation details).
- Parent and sibling items are always visible.
- In other words, if an item is visible in module foo, it’s visible in all the descendants of foo.
mod outer {
fn private() {
println!("outer::private");
}
pub fn public() {
println!("outer::public");
}
mod inner {
fn private() {
println!("outer::inner::private");
}
pub fn public() {
println!("outer::inner::public");
super::private();
}
}
}
fn main() {
outer::public();
}
outer::public
Notes
- Use the pub keyword to make modules public.
Additionally, there are advanced pub(…) specifiers to restrict the scope of public visibility.
- See the Rust Reference.
- Configuring pub(crate) visibility is a common pattern.
- Less commonly, you can give visibility to a specific path.
- In any case, visibility must be granted to an ancestor module (and all of its descendants).
Paths
Paths are resolved as follows:
- As a relative path:
- foo or self::foo refers to foo in the current module,
- super::foo refers to foo in the parent module.
- As an absolute path:
- crate::foo refers to foo in the root of the current crate,
- bar::foo refers to foo in the bar crate.
A module can bring symbols from another module into scope with use. You will typically see something like this at the top of each module:
use std::collections::HashSet;
use std::mem::transmute;
Filesystem Hierarchy
Omitting the module content will tell Rust to look for it in another file:
mod garden;
This tells rust that the garden module content is found at src/garden.rs. Similarly, a garden::vegetables module can be found at src/garden/vegetables.rs.
The crate root is in:
- src/lib.rs (for a library crate)
- src/main.rs (for a binary crate)
Modules defined in files can be documented, too, using “inner doc comments”. These document the item that contains them – in this case, a module.
//! This module implements the garden, including a highly performant germination
//! implementation.
// Re-export types from this module.
pub use seeds::SeedPacket;
pub use garden::Garden;
/// Sow the given seed packets.
pub fn sow(seeds: Vec<SeedPacket>) { todo!() }
/// Harvest the produce in the garden that is ready.
pub fn harvest(garden: &mut Garden) { todo!() }
Notes
- Before Rust 2018, modules needed to be located at module/mod.rs instead of module.rs, and this is still a working alternative for editions after 2018.
- The main reason to introduce filename.rs as alternative to filename/mod.rs was because many files named mod.rs can be hard to distinguish in IDEs.
- Deeper nesting can use folders, even if the main module is a file:
src/ ├── main.rs ├── top_module.rs └── top_module/ └── sub_module.rs
- The place rust will look for modules can be changed with a compiler directive:
#[path = "some/path.rs"] mod some_module;
This is useful, for example, if you would like to place tests for a module in a file named some_module_test.rs, similar to the convention in Go.