Defining Modules to Control Scope and Privacy
In this section, we’ll talk about modules and other parts of the module system,
namely paths that allow you to name items; the use
keyword that brings a
path into scope; and the pub
keyword to make items public. We’ll also discuss
the as
keyword, external packages, and the glob operator.
First, we’re going to start with a list of rules for easy reference when you’re organizing your code in the future. Then we’ll explain each of the rules in detail.
Modules Quick Reference
Here’s how modules, paths, the use
keyword, and the pub
keyword work in the
compiler, and how most developers organize their code. We’ll be going through
examples of each of these rules, but this is a great place to look in the
future as a reminder of how modules work.
- Start from the crate root: When compiling a crate, the compiler first looks in the crate root file (usually src/lib.rs for a library crate or src/main.rs for a binary crate).
- Declaring modules: In the crate root file, you can declare a new module
named, say, “garden”, with
mod garden;
. The compiler will look for the code inside the module in these places:- Inline, directly following
mod garden
, within curly brackets instead of the semicolon - In the file src/garden.rs
- In the file src/garden/mod.rs
- Inline, directly following
- Declaring submodules: In any file other than the crate root that’s being
compiled as part of the crate (for example, src/garden.rs), you can declare
submodules (for example,
mod vegetables;
). The compiler will look for the code inside submodules in these places within a directory named for the parent module:- Inline, directly following
mod vegetables
, within curly brackets instead of the semicolon - In the file src/garden/vegetables.rs
- In the file src/garden/vegetables/mod.rs
- Inline, directly following
- Paths to code in modules: Once a module is being compiled as part of your
crate, you can refer to code in that module (for example, an
Asparagus
type in the garden vegetables module) from anywhere else in this crate by using the pathcrate::garden::vegetables::Asparagus
as long as the privacy rules allow. - Private vs public: Code within a module is private from its parent
modules by default. To make a module public, declare it with
pub mod
instead ofmod
. To make items within a public module public as well, usepub
before their declarations. - The
use
keyword: Within a scope, theuse
keyword creates shortcuts to items to reduce repetition of long paths. In any scope that can refer tocrate::garden::vegetables::Asparagus
, you can create a shortcut withuse crate::garden::vegetables::Asparagus;
and then only need to writeAsparagus
to make use of that type in the scope.
Here’s a binary crate named backyard
that illustrates these rules. The
crate’s directory, also named backyard
, contains these files and directories:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
The crate root file, in this case src/main.rs, contains:
Filename: src/main.rs
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {:?}!", plant);
}
The pub mod garden;
means the compiler includes the code it finds in
src/garden.rs, which is:
Filename: src/garden.rs
pub mod vegetables;
And pub mod vegetables;
means the code in src/garden/vegetables.rs is
included too:
#[derive(Debug)]
pub struct Asparagus {}
Now let’s get into the details of these rules and demonstrate them in action!
Grouping Related Code in Modules
Modules let us organize code within a crate into groups for readability and easy reuse. Modules also control the privacy of items, which is whether an item can be used by outside code (public) or is an internal implementation detail and not available for outside use (private).
As an example, let’s write a library crate that provides the functionality of a restaurant. We’ll define the signatures of functions but leave their bodies empty to concentrate on the organization of the code, rather than actually implement a restaurant in code.
In the restaurant industry, some parts of a restaurant are referred to as front of house and others as back of house. Front of house is where customers are; this is where hosts seat customers, servers take orders and payment, and bartenders make drinks. Back of house is where the chefs and cooks work in the kitchen, dishwashers clean up, and managers do administrative work.
To structure our crate in the same way that a real restaurant works, we can
organize the functions into nested modules. Create a new library named
restaurant
by running cargo new --lib restaurant
; then put the code in
Listing 7-1 into src/lib.rs to define some modules and function signatures.
Filename: src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
We define a module by starting with the mod
keyword and then specify the
name of the module (in this case, front_of_house
) and place curly brackets
around the body of the module. Inside modules, we can have other modules, as in
this case with the modules hosting
and serving
. Modules can also hold
definitions for other items, such as structs, enums, constants, traits, or—as
in Listing 7-1—functions.
By using modules, we can group related definitions together and name why they’re related. Programmers using this code would have an easier time finding the definitions they wanted to use because they could navigate the code based on the groups rather than having to read through all the definitions. Programmers adding new functionality to this code would know where to place the code to keep the program organized.
Earlier, we mentioned that src/main.rs and src/lib.rs are called crate
roots. The reason for their name is that the contents of either of these two
files form a module named crate
at the root of the crate’s module structure,
known as the module tree.
Listing 7-2 shows the module tree for the structure in Listing 7-1.
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
This tree shows how some of the modules nest inside one another (for example,
hosting
nests inside front_of_house
). The tree also shows that some modules
are siblings to each other, meaning they’re defined in the same module
(hosting
and serving
are defined within front_of_house
). To continue the
family metaphor, if module A is contained inside module B, we say that module A
is the child of module B and that module B is the parent of module A.
Notice that the entire module tree is rooted under the implicit module named
crate
.
The module tree might remind you of the filesystem’s directory tree on your computer; this is a very apt comparison! Just like directories in a filesystem, you use modules to organize your code. And just like files in a directory, we need a way to find our modules.