Chapter 7
Packages, Crates, and Modules
As programs grow, organizing code matters: group related functionality, separate distinct features, and split one module in one file into multiple modules and then multiple files. A package can contain multiple binary crates and optionally one library crate; parts can be extracted into separate crates that become external dependencies (very large sets of interrelated packages use Cargo workspaces, Chapter 14). Encapsulation lets other code call yours through its public interface while private details stay yours to change. Scope decides which names are defined at each spot; two items can’t share a name in one scope. Together these features form the module system:
- Packages: a Cargo feature that lets you build, test, and share crates
- Crates: a tree of modules that produces a library or executable
- Modules and use: control the organization, scope, and privacy of paths
- Paths: a way of naming an item, such as a struct, function, or module
- 7.1 Packages and Crates
- 7.2 Control Scope and Privacy with Modules
- 7.3 Paths for Referring to an Item in the Module Tree
- 7.4 Bringing Paths into Scope with the use Keyword
- 7.5 Separating Modules into Different Files
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
1
Chapter 7 · 7.1 Packages and Crates
Crates: binary and library
The first parts of the module system are packages and crates.
Definition 7.1 (Crate). The smallest amount of code that the Rust compiler considers at a time. Even if you run rustc rather than cargo on a single source file (as in Chapter 1), the compiler considers that file to be a crate. Crates can contain modules, and the modules may be defined in other files that get compiled with the crate.
A crate comes in one of two forms:
Definition 7.2 (Binary crate). A program you can compile to an executable that you can run, such as a command line program or a server. Each must have a function called main that defines what happens when the executable runs. All the crates created so far have been binary crates.
Definition 7.3 (Library crate). A crate with no main function that doesn’t compile to an executable; it defines functionality intended to be shared with multiple projects — e.g. the rand crate from Chapter 2, which generates random numbers.
Most of the time when Rustaceans say “crate,” they mean library crate, and they use “crate” interchangeably with the general programming concept of a “library.”
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
2
Chapter 7 · 7.1 Packages and Crates
Crate roots and packages
Definition 7.4 (Crate root). A source file that the Rust compiler starts from and that makes up the root module of your crate (modules are explained in 7.2).
Definition 7.5 (Package). A bundle of one or more crates that provides a set of functionality. A package contains a Cargo.toml file that describes how to build those crates.
Cargo itself is a package: it contains the binary crate for the command line tool you’ve been using to build your code, and also a library crate that the binary crate depends on. Other projects can depend on the Cargo library crate to use the same logic the Cargo command line tool uses.
Rules on what a package may contain:
- as many binary crates as you like;
- at most only one library crate;
- at least one crate, whether that’s a library or binary crate.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
3
Chapter 7 · 7.1 Packages and Crates
Creating a package with cargo new
Let’s walk through what happens when we create a package. First, enter the command cargo new my-project:
$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
After cargo new my-project, we use ls to see what Cargo creates:
- In the my-project directory there’s a Cargo.toml file, giving us a package.
- There’s also a src directory that contains main.rs.
- Open Cargo.toml in your text editor and note that there’s no mention of src/main.rs: Cargo finds it by convention (next slide).
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
4
Chapter 7 · 7.1 Packages and Crates
How Cargo finds crate roots
Cargo follows conventions instead of listing crate roots in Cargo.toml, and passes the crate root files to rustc to build the library or binary.
Table 7.1: Cargo’s crate-root conventions
| File in the package | Meaning |
|---|---|
| src/main.rs | Crate root of a binary crate with the same name as the package |
| src/lib.rs | Crate root of a library crate with the same name as the package |
| src/bin/*.rs | Each file is a separate binary crate |
- Our my-project package only contains src/main.rs, so it only contains a binary crate named my-project.
- If a package contains src/main.rs and src/lib.rs, it has two crates: a binary and a library, both with the same name as the package.
- A package can have multiple binary crates by placing files in the src/bin directory: each file will be a separate binary crate.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
5
Chapter 7 · 7.2 Control Scope and Privacy with Modules
Modules cheat sheet: crate root and modules
This section covers modules and the other parts of the module system: paths, which name items; the use keyword that brings a path into scope; the pub keyword to make items public; plus the as keyword, external packages, and the glob operator.
First, a quick reference on how modules, paths, use and pub work in the compiler, and how most developers organize their code — a place to refer back to as a reminder:
- 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 and src/main.rs for a binary crate) for code to compile.
- Declaring modules. In the crate root file, you can declare new modules; say you declare a “garden” module with mod garden;. The compiler looks for the module’s code in these places:
- inline, within curly brackets that replace the semicolon following mod garden;
- in the file src/garden.rs;
- in the file src/garden/mod.rs.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
6
Chapter 7 · 7.2 Control Scope and Privacy with Modules
Modules cheat sheet: submodules, paths, privacy, use
- Declaring submodules. In any file other than the crate root, you can declare submodules — e.g. mod vegetables; in src/garden.rs. The compiler looks for the submodule’s code within the 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.
- Paths to code in modules. Once a module is part of your crate, you can refer to code in that module from anywhere else in that same crate, as long as the privacy rules allow, using the path to the code. An Asparagus type in the garden vegetables module would be found at crate::garden::vegetables::Asparagus.
- 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 of mod. To make items within a public module public as well, use pub before their declarations.
- The use keyword. Within a scope, use creates shortcuts to items to reduce repetition of long paths. In any scope that can refer to crate::garden::vegetables::Asparagus, use crate::garden::vegetables::Asparagus; creates a shortcut, and from then on you only need to write Asparagus in the scope.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
7
Chapter 7 · 7.2 Control Scope and Privacy with Modules
The rules in one crate: backyard
A binary crate named backyard illustrates these rules. The crate’s directory, also named backyard, contains these files and directories, and the crate root file is src/main.rs:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
…so src/garden/vegetables.rs is included too:
#[derive(Debug)]
pub struct Asparagus {}
src/main.rs
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {plant:?}!");
}
pub mod garden; includes src/garden.rs, where pub mod vegetables;…
pub mod vegetables;
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
8
Chapter 7 · 7.2 Control Scope and Privacy with Modules
Grouping related code in modules
Definition 7.6 (Module). A unit that organizes code within a crate for readability and easy reuse. Modules also control the privacy of items: code within a module is private by default.
Private items are internal implementation details not available for outside use. We can choose to make modules and the items within them public, which exposes them to allow external code to use and depend on them.
As an example, we write a library crate that provides the functionality of a restaurant. We define the signatures of functions but leave their bodies empty, to concentrate on the organization of the code rather than the implementation of a restaurant. In the restaurant industry:
- Front of house is where customers are: where the 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.
We organize its functions into nested modules; create the library with cargo new restaurant --lib.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
9
Chapter 7 · 7.2 Control Scope and Privacy with Modules
Defining modules with mod
Listing 7.1: A front_of_house module containing other modules that then contain functions — 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() {}
}
}
This code, entered into src/lib.rs, is the front of house section.
- A module is defined with the mod keyword followed by its name (here front_of_house); its body goes inside curly brackets.
- Modules can hold other modules (here hosting and serving) and definitions of other items: structs, enums, constants, traits, and functions.
- Grouping related definitions names why they’re related: readers navigate by group instead of reading every definition, and contributors know where to place new code to keep the program organized.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
10
Chapter 7 · 7.2 Control Scope and Privacy with Modules
The module tree
Earlier we said that src/main.rs and src/lib.rs are called crate roots. The reason for the name: the contents of either file form a module named crate at the root of the crate’s module structure.
Definition 7.7 (Module tree). The crate’s module structure: the modules and items nested inside one another, rooted under the implicit module named crate.
Listing 7.2 shows the module tree for the structure in Listing 7.1.
Listing 7.2: The module tree for the code in Listing 7.1
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
11
Chapter 7 · 7.2 Control Scope and Privacy with Modules
Reading the module tree
The tree in Listing 7.2 shows how modules relate:
- Nesting. Some modules nest inside other modules; for example, hosting nests inside front_of_house.
- Siblings. Modules defined in the same module are siblings: hosting and serving are siblings defined within front_of_house.
- Child and parent. If module A is contained inside module B, module A is the child of module B, and module B is the parent of module A.
- Root. The entire module tree is rooted under the implicit module named crate.
Example 7.8. The module tree is like the filesystem’s directory tree on your computer — 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: that is what paths are for (7.3).
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
12
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Absolute and relative paths
To show Rust where to find an item in a module tree, we use a path in the same way we use a path when navigating a filesystem. To call a function, we need to know its path. A path can take two forms:
Definition 7.9 (Absolute path). The full path starting from a crate root. For code from an external crate, the absolute path begins with the crate name; for code from the current crate, it starts with the literal crate.
Definition 7.10 (Relative path). A path that starts from the current module and uses self, super, or an identifier in the current module.
Both absolute and relative paths are followed by one or more identifiers separated by double colons (::).
Returning to Listing 7.1: say we want to call the add_to_waitlist function. This is the same as asking: what’s the path of the add_to_waitlist function?
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
13
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Calling add_to_waitlist two ways
Listing 7.3: Calling the add_to_waitlist function using absolute and relative paths — src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
- Listing 7.3 is Listing 7.1 with some of the modules and functions removed.
- It shows two ways to call add_to_waitlist from a new function, eat_at_restaurant, defined in the crate root.
- These paths are correct, but another problem remains that prevents this example from compiling as is — explained shortly.
- eat_at_restaurant is part of our library crate’s public API, so we mark it with the pub keyword (more on pub later in this section).
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
14
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Absolute or relative: which to choose
First call: absolute
add_to_waitlist is in the same crate as eat_at_restaurant, so the path can start with the crate keyword, then include each successive module down to add_to_waitlist. Filesystem equivalent: /front_of_house/hosting/add_to_waitlist — starting with crate is like starting from the filesystem root / in your shell.
Second call: relative
The path starts with front_of_house, the name of the module defined at the same level of the module tree as eat_at_restaurant. Filesystem equivalent: front_of_house/hosting/add_to_waitlist. Starting with a module name means that the path is relative.
The choice depends on whether you’re more likely to move an item’s definition separately from or together with the code that uses it:
- Move front_of_house and eat_at_restaurant together into a module customer_experience: the absolute path must be updated; the relative path stays valid.
- Move only eat_at_restaurant into a module dining: the absolute path stays the same; the relative path must be updated.
The book generally prefers absolute paths: it’s more likely we’ll want to move definitions and item calls independently.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
15
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Error: module hosting is private
Let’s try to compile Listing 7.3 and find out why it won’t compile yet:
Listing 7.4: Compiler errors from building the code in Listing 7.3 (first error)
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: module `hosting` is private
--> src/lib.rs:9:28
|
9 | crate::front_of_house::hosting::add_to_waitlist();
| ^^^^^^^ --------------- function `add_to_waitlist` is not publicly re-exported
| |
| private module
|
note: the module `hosting` is defined here
--> src/lib.rs:2:5
|
2 | mod hosting {
| ^^^^^^^^^^^
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
16
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
The same error for the relative path
The relative path in the second call fails the same way:
Listing 7.4 (continued): Compiler errors from building the code in Listing 7.3 (second error)
error[E0603]: module `hosting` is private
--> src/lib.rs:12:21
|
12 | front_of_house::hosting::add_to_waitlist();
| ^^^^^^^ --------------- function `add_to_waitlist` is not publicly re-exported
| |
| private module
|
note: the module `hosting` is defined here
--> src/lib.rs:2:5
|
2 | mod hosting {
| ^^^^^^^^^^^
For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant` (lib) due to 2 previous errors
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
17
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Everything is private by default
The errors say module hosting is private. We have the correct paths for the hosting module and the add_to_waitlist function, but Rust won’t let us use them because it doesn’t have access to the private sections.
Definition 7.11 (Privacy rule). In Rust, all items (functions, methods, structs, enums, modules, and constants) are private to parent modules by default. Items in a parent module can’t use the private items inside child modules, but items in child modules can use the items in their ancestor modules. To make an item like a function or struct private, put it in a module.
Why: child modules wrap and hide their implementation details, but they can see the context in which they’re defined. Rust made hiding inner implementation details the default, so you know which parts of the inner code you can change without breaking the outer code. The pub keyword is the option to expose inner parts of child modules to outer ancestor modules.
Example 7.12. Think of the privacy rules as the back office of a restaurant: what goes on in there is private to restaurant customers, but office managers can see and do everything in the restaurant they operate.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
18
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Exposing paths with pub: a public module
Listing 7.5: Declaring the hosting module as pub to use it from eat_at_restaurant — src/lib.rs
mod front_of_house {
pub mod hosting {
fn add_to_waitlist() {}
}
}
// -- snip --
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
Back to the error in Listing 7.4 that told us the hosting module is private.
We want eat_at_restaurant in the parent module to have access to add_to_waitlist in the child module, so we mark the hosting module with the pub keyword.
Unfortunately, the code in Listing 7.5 still results in compiler errors, shown in Listing 7.6.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
19
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Error: function add_to_waitlist is private
Listing 7.6: Compiler errors from building the code in Listing 7.5 (first error)
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: function `add_to_waitlist` is private
--> src/lib.rs:10:37
|
10 | crate::front_of_house::hosting::add_to_waitlist();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `add_to_waitlist` is defined here
--> src/lib.rs:3:9
|
3 | fn add_to_waitlist() {}
| ^^^^^^^^^^^^^^^^^^^^
This time the error points at add_to_waitlist itself, not at the module hosting.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
20
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
The same error for the relative path
Listing 7.6 (continued): Compiler errors from building the code in Listing 7.5 (second error)
error[E0603]: function `add_to_waitlist` is private
--> src/lib.rs:13:30
|
13 | front_of_house::hosting::add_to_waitlist();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `add_to_waitlist` is defined here
--> src/lib.rs:3:9
|
3 | fn add_to_waitlist() {}
| ^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant` (lib) due to 2 previous errors
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
21
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
A public module doesn’t make its contents public
What happened? Adding the pub keyword in front of mod hosting makes the module public. With this change, if we can access front_of_house, we can access hosting.
But the contents of hosting are still private; making the module public doesn’t make its contents public. The pub keyword on a module only lets code in its ancestor modules refer to it, not access its inner code.
Because modules are containers, there’s not much we can do by only making the module public; we need to go further and choose to make one or more of the items within the module public as well.
Remark. The errors in Listing 7.6 say that the add_to_waitlist function is private. The privacy rules apply to structs, enums, functions, and methods as well as modules.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
22
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Making the function public too
Listing 7.7: Adding the pub keyword to mod hosting and fn add_to_waitlist lets us call the function from eat_at_restaurant — src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// -- snip --
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
Let’s also make the add_to_waitlist function public by adding the pub keyword before its definition.
Now the code will compile!
To see why adding pub lets us use these paths in eat_at_restaurant with respect to the privacy rules, the next slide walks through the absolute and the relative paths step by step.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
23
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Why both paths now work
The absolute path
- It starts with crate, the root of our crate’s module tree.
- front_of_house is defined in the crate root. It isn’t public, but eat_at_restaurant is defined in the same module (they are siblings), so we can refer to it.
- hosting is marked pub, and we can access its parent module, so we can access hosting.
- add_to_waitlist is marked pub, and we can access its parent module, so this function call works!
The relative path
Same logic except the first step: the path starts from front_of_house, which is defined within the same module as eat_at_restaurant. Then, because hosting and add_to_waitlist are pub, the rest of the path works, and the call is valid!
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
24
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Public APIs, and packages with a binary and a library
If you share your library crate so other projects can use your code, your public API is your contract with users of your crate: it determines how they can interact with your code. Managing changes to a public API so people can depend on your crate is beyond the scope of this book; see the Rust API Guidelines (rust-lang.github.io/api-guidelines).
Best practices for packages with a binary and a library
- A package can contain both a src/main.rs binary crate root and a src/lib.rs library crate root; both crates have the package name by default.
- Typically the binary crate has just enough code to start an executable that calls code defined in the library crate, so other projects benefit from the most functionality, because the library crate’s code can be shared.
- Define the module tree in src/lib.rs. Public items can then be used in the binary crate by starting paths with the name of the package.
- The binary crate becomes a user of the library crate just like a completely external crate: it can only use the public API. This helps you design a good API — you’re not only the author but also a client!
Remark. Chapter 12 demonstrates this organizational practice with a command line program containing both a binary crate and a library crate.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
25
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Starting relative paths with super
Definition 7.13 (super). A keyword at the start of a path that makes it a relative path beginning in the parent module, rather than the current module or the crate root — like starting a filesystem path with .. to go to the parent directory.
Listing 7.8: Calling a function using a relative path starting with super — src/lib.rs
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}
Using super references an item we know is in the parent module, which makes rearranging the module tree easier when the module is closely related to the parent but the parent might be moved elsewhere someday.
A chef fixes an incorrect order and personally brings it out. From back_of_house, super goes to its parent — crate, the root — where deliver_order is found. Success!
back_of_house and deliver_order are likely to stay in the same relationship and get moved together, so super leaves fewer places to update if the code moves.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
26
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Making structs public
Listing 7.9: A struct with some public fields and some private fields — src/lib.rs
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
pub before a struct definition makes the struct public, but its fields stay private. Each field can be made public or not on a case-by-case basis.
Here back_of_house::Breakfast is public, with a public toast field but a private seasonal_fruit field. In the restaurant, the customer picks the bread that comes with a meal, but the chef decides which fruit accompanies it based on what’s in season and in stock. The fruit changes quickly, so customers can’t choose it or even see which fruit they’ll get.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
27
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Using public and private fields
Listing 7.9 (continued): A struct with some public fields and some private fields — src/lib.rs
pub fn eat_at_restaurant() {
// Order a breakfast in the summer with Rye toast.
let mut meal = back_of_house::Breakfast::summer("Rye");
// Change our mind about what bread we'd like.
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
// The next line won't compile if we uncomment it; we're not allowed
// to see or modify the seasonal fruit that comes with the meal.
// meal.seasonal_fruit = String::from("blueberries");
}
Because the toast field in back_of_house::Breakfast is public, eat_at_restaurant can write and read it using dot notation. It can’t use the seasonal_fruit field, because seasonal_fruit is private. Try uncommenting the line that modifies seasonal_fruit to see what error you get!
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
28
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Constructors for private fields; public enums
Because back_of_house::Breakfast has a private field, the struct needs to provide a public associated function that constructs an instance of Breakfast (here named summer). Without such a function, we couldn’t create an instance of Breakfast in eat_at_restaurant, because we couldn’t set the value of the private seasonal_fruit field there.
In contrast, if we make an enum public, all of its variants are then public. We only need the pub before the enum keyword.
Listing 7.10: Designating an enum as public makes all its variants public. — src/lib.rs
mod back_of_house {
pub enum Appetizer {
Soup,
Salad,
}
}
pub fn eat_at_restaurant() {
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
}
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
29
Chapter 7 · 7.3 Paths for Referring to an Item in the Module Tree
Why enums and structs differ
Because we made the Appetizer enum public, we can use the Soup and Salad variants in eat_at_restaurant.
Table 7.2: What pub on a struct or an enum makes public
| Item | pub on the definition | Why this default |
|---|---|---|
| struct | The struct only; each field stays private unless marked pub | Structs are often useful without their fields being public |
| enum | The enum and all of its variants | Enums aren’t very useful unless their variants are public |
It would be annoying to annotate all enum variants with pub in every case, so the default for enum variants is to be public. Struct fields follow the general rule: everything is private by default unless annotated with pub.
Remark. One more pub situation involves the last feature, use: first use alone, then pub with use.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
30
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Creating a shortcut with use
Writing out paths to call functions can feel inconvenient and repetitive: in Listing 7.7, every call to add_to_waitlist had to specify front_of_house and hosting too. Instead, we can create a shortcut to a path with the use keyword once and then use the shorter name everywhere else in the scope.
Example 7.14. use plus a path is like a symbolic link in the filesystem: after use crate::front_of_house::hosting in the crate root, hosting is a valid name there, as though the module had been defined in the crate root. Paths brought into scope with use also check privacy, like any other paths.
Listing 7.11: Bringing a module into scope with use — src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
31
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
A use shortcut applies only in its own scope
Listing 7.12: A use statement only applies in the scope it’s in. — src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
mod customer {
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
}
use only creates the shortcut for the particular scope in which the use occurs.
Listing 7.12 moves the eat_at_restaurant function into a new child module named customer. That is a different scope than the use statement, so the function body won’t compile.
The compiler error on the next slides shows that the shortcut no longer applies within the customer module.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
32
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Error: cannot find hosting in this scope
Building Listing 7.12 — the error:
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0433]: cannot find module or crate `hosting` in this scope
--> src/lib.rs:11:9
|
11 | hosting::add_to_waitlist();
| ^^^^^^^ use of unresolved module or unlinked crate `hosting`
|
= help: if you wanted to use a crate named `hosting`,
use `cargo add hosting` to add it to your `Cargo.toml`
help: consider importing this module through its public re-export
|
10 + use crate::hosting;
|
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
33
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
…and an unused import warning
warning: unused import: `crate::front_of_house::hosting`
--> src/lib.rs:7:5
|
7 | use crate::front_of_house::hosting;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
For more information about this error, try `rustc --explain E0433`.
warning: `restaurant` (lib) generated 1 warning
error: could not compile `restaurant` (lib) due to 1 previous error; 1 warning emitted
Notice there’s also a warning that the use is no longer used in its scope! To fix this problem, move the use within the customer module too, or reference the shortcut in the parent module with super::hosting within the child customer module.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
34
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Idiomatic use paths for functions
Listing 7.13: Bringing the add_to_waitlist function into scope with use, which is unidiomatic — src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
}
Why did Listing 7.11 write use crate::front_of_house::hosting and call hosting::add_to_waitlist, rather than taking the use path all the way out to the function, as in Listing 7.13?
Both work, but Listing 7.11 is idiomatic: bringing the parent module into scope means specifying it when calling the function, which makes clear the function isn’t locally defined while still minimizing repetition of the full path. Listing 7.13 is unclear as to where add_to_waitlist is defined.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
35
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Idiomatic use paths for structs and enums
On the other hand, when bringing in structs, enums, and other items with use, it’s idiomatic to specify the full path. Listing 7.14 shows the idiomatic way to bring the standard library’s HashMap struct into the scope of a binary crate.
Listing 7.14: Bringing HashMap into scope in an idiomatic way — src/main.rs
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
Remark. There’s no strong reason behind this idiom: it’s just the convention that has emerged, and folks have gotten used to reading and writing Rust code this way.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
36
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
The exception: two items with the same name
Listing 7.15: Bringing two types with the same name into the same scope requires using their parent modules. — src/lib.rs
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
// --snip--
Ok(())
}
fn function2() -> io::Result<()> {
// --snip--
Ok(())
}
The exception to the full-path idiom: bringing two items with the same name into scope with use statements, because Rust doesn’t allow that.
Listing 7.15 brings two Result types with different parent modules into scope and refers to them through those parents, which distinguishes them.
If instead we specified use std::fmt::Result and use std::io::Result, we’d have two Result types in the same scope, and Rust wouldn’t know which one we meant when we used Result.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
37
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Providing new names with the as keyword
Listing 7.16: Renaming a type when it’s brought into scope with the as keyword — src/lib.rs
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
Ok(())
}
fn function2() -> IoResult<()> {
// --snip--
Ok(())
}
Another solution to bringing two types of the same name into the same scope with use: after the path, specify as and a new local name.
Definition 7.15 (Alias). A new local name for a type, given with as after the path in a use statement.
Listing 7.16 rewrites Listing 7.15: the second use chooses the new name IoResult for std::io::Result, which won’t conflict with the Result from std::fmt also in scope. Listings 7.15 and 7.16 are both considered idiomatic, so the choice is up to you!
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
38
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Re-exporting names with pub use
A name brought into scope with use is private to the scope into which we imported it. To let code outside that scope refer to the name as if it had been defined in that scope, combine pub and use.
Definition 7.16 (Re-exporting). Bringing an item into scope with pub use, which also makes that item available for others to bring into their scope.
Listing 7.17 is Listing 7.11 with use in the root module changed to pub use.
Listing 7.17: Making a name available for any code to use from a new scope with pub use — src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
39
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
What re-exporting changes for callers
Table 7.3: The path external code uses to call add_to_waitlist
| When | Path for external code |
|---|---|
| Before pub use | restaurant::front_of_house::hosting::add_to_waitlist() — also requires front_of_house to be pub |
| After pub use | restaurant::hosting::add_to_waitlist() |
Re-exporting is useful when the internal structure of your code is different from how programmers calling your code would think about the domain. In the restaurant metaphor, the people running the restaurant think about “front of house” and “back of house,” but customers visiting probably won’t think about the parts of the restaurant in those terms.
With pub use, we can write our code with one structure but expose a different structure. That makes the library well organized both for programmers working on the library and for programmers calling it.
Remark. “Exporting a Convenient Public API” in Chapter 14 shows another example of pub use and how it affects your crate’s documentation.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
40
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Using external packages
The Chapter 2 guessing game used an external package called rand to get random numbers. To use rand in the project, we added this line to Cargo.toml:
Cargo.toml
rand = "0.10.1"
Adding rand as a dependency in Cargo.toml tells Cargo to download the rand package and any dependencies from crates.io and make rand available to our project.
Then, to bring rand definitions into the scope of our package, we added a use line starting with the name of the crate, rand, and listed the items we wanted to bring into scope. In “Generating a Random Number” (Chapter 2) we brought the items in the rand::prelude module into scope and called the rand::rng function — shown on the next two slides.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
41
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Bringing rand items into scope
The use line starts with the crate name rand:
use std::io;
use rand::prelude::*;
fn main() {
println!("Guess the number!");
let secret_number = rand::rng().random_range(1..=100);
println!("The secret number is: {secret_number}");
println!("Please input your guess.");
let mut guess = String::new();
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
42
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Any crate from crates.io works the same way
The rest of the snippet (continued):
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
}
Members of the Rust community have made many packages available at crates.io, and pulling any of them into your package involves these same steps:
- list them in your package’s Cargo.toml file;
- use use to bring items from their crates into scope.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
43
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
The standard library is external too
The standard std library is also a crate that’s external to our package. Because the standard library is shipped with the Rust language, we don’t need to change Cargo.toml to include std. But we do need to refer to it with use to bring items from there into our package’s scope. For example, with HashMap:
use std::collections::HashMap;
This is an absolute path starting with std, the name of the standard library crate.
Using nested paths to clean up use lists
When using multiple items defined in the same crate or same module, listing each item on its own line can take up a lot of vertical space in our files. For example, the guessing game in Listing 2.4 has two use statements that bring items from std into scope (next two slides).
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
44
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Before: one use line per item
src/main.rs — two separate use statements for std
// --snip--
use std::cmp::Ordering;
use std::io;
// --snip--
use rand::prelude::*;
fn main() {
println!("Guess the number!");
let secret_number = rand::rng().random_range(1..=100);
println!("The secret number is: {secret_number}");
println!("Please input your guess.");
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
45
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Before: one use line per item (continued)
src/main.rs (continued)
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
Instead, nested paths bring the same items into scope in one line: the common part of the path, two colons, then curly brackets around a list of the parts of the paths that differ.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
46
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
After: a nested path
Listing 7.18: Specifying a nested path to bring multiple items with the same prefix into scope — src/main.rs
use rand::prelude::*;
// --snip--
use std::{cmp::Ordering, io};
// --snip--
fn main() {
println!("Guess the number!");
let secret_number = rand::rng().random_range(1..=100);
println!("The secret number is: {secret_number}");
println!("Please input your guess.");
let mut guess = String::new();
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
47
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
After: a nested path (continued)
Listing 7.18 (continued): Specifying a nested path — src/main.rs
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse().expect("Please type a number!");
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
In bigger programs, nested paths can reduce the number of separate use statements needed by a lot!
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
48
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Nesting at any level, with self
A nested path can be used at any level in a path, which is useful when combining two use statements that share a subpath.
Listing 7.19 has two use statements: one brings std::io into scope and one brings std::io::Write into scope.
Listing 7.19: Two use statements where one is a subpath of the other — src/lib.rs
use std::io;
use std::io::Write;
The common part of these two paths is std::io, and that’s the complete first path. To merge them into one use statement, use self in the nested path:
Listing 7.20: Combining the paths in Listing 7.19 into one use statement — src/lib.rs
use std::io::{self, Write};
This line brings std::io and std::io::Write into scope.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
49
Chapter 7 · 7.4 Bringing Paths into Scope with the use Keyword
Importing items with the glob operator
Definition 7.17 (Glob operator). The * written after a path in a use statement; it brings all public items defined in that path into scope.
use std::collections::*;
This use statement brings all public items defined in std::collections into the current scope. Be careful when using the glob operator:
- Glob can make it harder to tell what names are in scope and where a name used in your program was defined.
- If the dependency changes its definitions, what you’ve imported changes as well — e.g. compiler errors when you upgrade a dependency that adds a definition with the same name as one of yours in the same scope.
Glob is often used when testing, to bring everything under test into the tests module (Chapter 11), and sometimes as part of the prelude pattern (see the standard library documentation).
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
50
Chapter 7 · 7.5 Separating Modules into Different Files
Extracting front_of_house into its own file
So far, every example defined multiple modules in one file. When modules get large, you might move their definitions to a separate file to make the code easier to navigate. Starting from Listing 7.17, we extract modules into files; here the crate root file is src/lib.rs, but the procedure also works with binary crates whose crate root file is src/main.rs.
First, extract front_of_house: remove the code inside its curly brackets, leaving only the mod front_of_house; declaration. This won’t compile until we create src/front_of_house.rs in Listing 7.22.
Listing 7.21: Declaring the front_of_house module whose body will be in src/front_of_house.rs — src/lib.rs
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
51
Chapter 7 · 7.5 Separating Modules into Different Files
The module body moves to src/front_of_house.rs
Next, place the code that was in the curly brackets into a new file named src/front_of_house.rs. The compiler knows to look in this file because it came across the module declaration in the crate root with the name front_of_house.
Listing 7.22: Definitions inside the front_of_house module in src/front_of_house.rs — src/front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {}
}
Remark. You only need to load a file using a mod declaration once in your module tree. Once the compiler knows the file is part of the project (and where in the module tree the code resides, because of where you put the mod statement), other files should refer to the loaded file’s code using a path to where it was declared (7.3). In other words, mod is not an “include” operation that you may have seen in other programming languages.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
52
Chapter 7 · 7.5 Separating Modules into Different Files
Extracting a child module: hosting
Extracting hosting is a bit different, because hosting is a child module of front_of_house, not of the root module. Its file goes in a new directory named for its ancestors in the module tree, here src/front_of_house.
1. Change src/front_of_house.rs to contain only the declaration of the hosting module — src/front_of_house.rs
pub mod hosting;
2. Create a src/front_of_house directory and a hosting.rs file with the definitions made in the hosting module — src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}
Remark. If we instead put hosting.rs in the src directory, the compiler would expect the hosting.rs code to be in a hosting module declared in the crate root, not declared as a child of the front_of_house module. The compiler’s rules for which files to check for which modules’ code mean the directories and files more closely match the module tree.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
53
Chapter 7 · 7.5 Separating Modules into Different Files
Alternate file paths
So far we’ve covered the most idiomatic file paths the Rust compiler uses, but Rust also supports an older style of file path.
Table 7.4: Where the compiler looks for a module’s code
| Module | Idiomatic (what we covered) | Older style, still supported |
|---|---|---|
| front_of_house, declared in the crate root | src/front_of_house.rs | src/front_of_house/mod.rs |
| hosting, a submodule of front_of_house | src/front_of_house/hosting.rs | src/front_of_house/hosting/mod.rs |
- Using both styles for the same module gives a compiler error.
- Mixing both styles for different modules in the same project is allowed but might be confusing for people navigating your project.
- The main downside of the mod.rs style: your project can end up with many files named mod.rs, which can get confusing when you have them open in your editor at the same time.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
54
Chapter 7 · 7.5 Separating Modules into Different Files
Same module tree, different files
We’ve moved each module’s code to a separate file, and the module tree remains the same. The function calls in eat_at_restaurant work without any modification, even though the definitions live in different files. This technique lets you move modules to new files as they grow in size.
The pub use crate::front_of_house::hosting statement in src/lib.rs hasn’t changed either, and use has no impact on what files are compiled as part of the crate. The mod keyword declares modules, and Rust looks in a file with the same name as the module for the code that goes into that module.
The restaurant crate’s source files after the extraction
src
├── front_of_house
│ └── hosting.rs
├── front_of_house.rs
└── lib.rs
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
55
Chapter 7 · Summary
Chapter summary
Rust lets you organize code and control what other code can see:
- split a package into multiple crates, and a crate into modules, so that you can refer to items defined in one module from another module;
- refer to items by specifying absolute paths (starting from crate or a crate name) or relative paths (starting from self, super, or an identifier in the current module);
- bring paths into scope with a use statement, so you can use a shorter path for multiple uses of the item in that scope;
- rely on module code being private by default, and make definitions public by adding the pub keyword.
Remark. Next, Chapter 8 looks at some collection data structures in the standard library that you can use in your neatly organized code.
The Rust Programming Language · Ch.7 Packages, Crates, and Modules
56