Modules in Rust allow us to structure our code logically, group related functionality together, and manage visibility. They provide a way to organize code within a crate, making it more readable and maintainable.
Key concepts
1. Start from the Crate Root:
- When compiling a crate, the compiler begins by looking in the crate root file (usually `src/lib.rs` for a library crate or `src/main.rs` for a binary crate).
- The crate root file contains the initial code to compile and serves as the entry point for the module system.
2. Declaring Modules:
- In the crate root file, we can declare new modules using the `mod` keyword.
- For example, declaring a "garden" module: `mod garden;`.
- The compiler searches for the module's code in several 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`.
3. Declaring Submodules:
- In any file other than the crate root, we can declare submodules.
- For instance, declaring `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.
- In the file `src/garden/vegetables.rs`.
- In the file `src/garden/vegetables/mod.rs`.
4. Paths to Code in Modules:
- Once a module is part of your crate, you can refer to its code from anywhere else in the same crate.
- Use the path to the code, such as `crate::garden::vegetables::Asparagus`.
- Privacy rules apply: you can access code within the same crate as long as visibility allows.
5. Private vs. Public:
- By default, code within a module is private to its parent modules.
- To make a module public, declare it with `pub mod` instead of `mod`.
- Use `pub` before item declarations to make them public within a public module.
6. The `use` Keyword:
- Within a scope, `use` creates shortcuts to items, reducing repetition of long paths.
- For example, `use crate::garden::vegetables::Asparagus;`.
- Afterward, you only need to write `Asparagus` to use that type in the scope.
Example: Creating a Restaurant Library
Let's apply these concepts to a practical example. Imagine we're building a restaurant library. We'll define function signatures for restaurant operations without implementing their bodies. Our focus is on organizing the code.
Front of House Section:
Rust
// 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 create a module named `front_of_house`.
- Inside it, we have nested modules: `hosting` and `serving`.
- Modules allow us to group related functionality (e.g., seating customers, taking orders) and control visibility.
Module Tree:
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
- The entire module tree is rooted under the implicit module named `crate`.
- Just like directories in a filesystem, modules help organize our code.