| title | Modules |
|---|---|
| slug | modules |
- The Rust module tree is the hierarchical representation of all modules in a crate.
- It defines the namespace structure and governs how items are referenced via paths at compile time.
- Unlike some languages like Go and JavaScript, Rust's module tree is not purely based on the file system.
-
The root of the module tree begins with either
main.rs(in a binary crate) orlib.rs(in a library crate).[!tip] When a Rust package contains both
main.rsandlib.rs, Cargo builds two separate crates with isolated module trees: a library crate (lib.rs) and a binary crate (main.rs).- In an optimized incremental build,
lib.rsis compiled first. - Then,
main.rslinks to it as an external dependency.
- In an optimized incremental build,
-
A file or folder becomes part of the module tree only if it is explicitly declared with the
modkeyword in its parent module. -
A module can be defined in one of two ways:
- File module β
mod foo;(resolved to foo.rs or foo/mod.rs) - Inline module β
mod foo { ... }(defined directly inside a file)
- File module β
-
my_app
βββ Cargo.toml (package name my_app)
βββ src
βββ main.rs (binary crate)
βββ lib.rs (library crate)
βββ foo.rs (module foo, declared in lib.rs with `pub mod foo;`)
β (may contain inline modules via `mod x { ... }`)
β
βββ bar
β βββ mod.rs (module bar, declared in lib.rs with `pub mod bar;`)
β βββ file.rs (submodule of bar, declared in bar/mod.rs with `mod file;`)
β
βββ buzz.rs (module buzz, declared in lib.rs with `pub mod buzz;`)
βββ buzz
βββ file.rs (submodule of buzz, declared in buzz.rs with `mod file;`)Assume,
main.rsdepends onstd,rand(3rd party crate) and library crate.lib.rscontainspub mod foo;,pub mod bar;andpub mod buzz;.foo.rs,bar/file.rs,buzz/file.rscontainspub fn hello_world()bar/mod.rsandbuzz.rscontainsmod file;andpub use file::hello_world;
The module trees are,
lib.rs (library crate) (compiled as crate `my_app`)
crate::
βββ foo
βββ bar
βββ buzz
main.rs (binary crate) (depends on a compiled crate `my_app`)
crate::
(binary crate root)
extern prelude:
βββ std
βββ rand
available crates:
βββ my_app β library crate from lib.rsCargo automatically passes each dependency listed in Cargo.toml to the Rust compiler (rustc) using the --extern flag. The compiler then makes these crates available through the extern prelude. This was introduced in Rust 2018 to make external crates automatically available in every module without requiring extern crate declarations or explicit imports at the crate root.
Tip
- Private by default to outer levels.
- Accessible to same/child levels.
- Use
pubkeyword to exposes to outer levels.
- Everything inside a module is private by default to outer modules.
- However, all items are accessible from within the same module or from the nested modules.
- The
pubkeyword is used to make a module or its items public to outer modules.
To access elements at different levels, use the :: path operator with these patterns:
- Current module: direct item name or
self:: - Parent module:
super:: - Crate root:
crate:: - Cross-Crate:
my_app::(the package name), Ex. accessing the library crate from the binary crate. - External crates in
Cargo.toml: The full path with crate name (uuid::Uuid::new_v4()) or ausedeclaration (use uuid::Uuid) to import the elements to the current level first.
Tip
- The
pubkeyword makes an item fully public (exposing to all outer levels). - Visibility modifiers restrict access to specific scopes.
| Modifier | Visible To |
|---|---|
pub(self) |
Current module |
pub(super) |
Parent module |
pub(crate) |
Entire crate |
pub(in path) |
Specific ancestor module |
- Defines with the
modkeyword. - Rust supports nested modules.
fn main() {
inline::greet();
}
mod inline { // π‘ No pub keyword but can be accessible from the same level/ main.rs
pub fn greet() { // π‘ function has to be public to access from outside
println!("Hello, world!");
}
}fn main() {
inline::nested::greet();
}
mod inline {
pub mod nested { // π‘ Use `pub` keyword to access from outside
pub fn greet() {
println!("Hello, world!");
}
}
}fn main() {
inline::greet();
inline::nested::greet();
inline::nested::self_greet();
inline::nested::super_greet();
inline::nested::super_greet_private();
inline::nested_private_greet();
inline2::inline_greet();
}
mod inline {
pub fn greet() {
println!("Inline");
}
fn greet_private() { // βοΈ public mod: private fn; can call from same level or child mod level
println!("Inline π");
}
// βοΈ pub mod: pub fn; can call directly from outside
pub mod nested {
pub fn greet() {
println!("Nested");
}
pub fn self_greet() {
self::greet(); // π‘ or greet() ; call inline::nested::greet
}
pub fn super_greet() {
super::greet(); // π‘ call inline::greet
}
pub fn super_greet_private() {
super::greet_private();
}
}
// βοΈ private mod: pub fn ; can call from parent level
mod nested_private {
pub fn greet() {
println!("Nested πmod");
}
}
pub fn nested_private_greet() {
nested_private::greet();
}
}
mod inline2 {
use crate::inline; // π‘ or `use super::inline;` reexport inline into the module.
pub fn inline_greet() {
inline::greet(); // π‘ if not reexport, crate::inline::greet(); or super::inline::greet();
}
}fn main() {
inline::nested_pub_super();
inline::nested_pub_crate();
inline::nested_pub_in_path();
inline::nested::exposed_pub_self();
inline::nested::exposed_parent_pub_self();
inline::nested::pub_crate();
}
mod inline {
pub mod nested {
// Accessible only within current module or sub modules
pub(self) fn pub_self() {
println!("Nested self");
}
// Accessible only from inside or super/ 1st parent
pub(super) fn pub_super() {
println!("Nested super");
}
// Accessible anywhere within the current crate
pub(crate) fn pub_crate() {
println!("Nested crate");
}
// Accessible only within `crate::inline` and its submodules
pub(in crate::inline) fn pub_in_path() {
println!("Nested in-path");
}
// Exposed pub(self), but Clippy triggers a warning "unnecessary `pub(self)`"
pub fn exposed_pub_self() {
pub_self()
}
// Exposed parent's pub(self), but Clippy triggers a warning "unnecessary `pub(self)`"
pub fn exposed_parent_pub_self() {
super::pub_self()
}
}
pub(self) fn pub_self() {
println!("Inline Self");
}
pub fn nested_pub_super() {
nested::pub_super()
}
pub fn nested_pub_crate() {
nested::pub_crate()
}
pub fn nested_pub_in_path() {
nested::pub_in_path()
}
}When writing tests itβs a good practice to write tests inside a test module because they compile only when running tests.
#![allow(unused)]
fn greet() -> String {
"Hello, world!".to_string()
}
#[cfg(test)] // Only compiles when running tests
mod tests {
use super::greet; // Import root greet function
#[test]
fn test_greet() {
assert_eq!("Hello, world!", greet());
}
}βββ Cargo.toml (package name my_app)
βββ src
βββ main.rs
βββ lib.rs
βββ foo.rs{{< tabs count=3 group="mod-files-in-same-dir" label="Select File" >}} {{< tab label="main.rs" >}}
mod foo;
fn main() {
my_app::greet();
my_app::inline::greet();
foo::greet();
foo::inline::greet();
}{{< /tab >}} {{< tab label="lib.rs" >}}
pub fn greet() {
println!("Lib");
}
pub mod inline {
pub fn greet() {
println!("Lib:Inline");
}
}{{< /tab >}} {{< tab label="foo.rs" >}}
pub fn greet() {
println!("Foo");
}
pub mod inline {
pub fn greet() {
println!("Foo:Inline");
}
}{{< /tab >}} {{< /tabs >}}
Tip
- Accessing the library crate from the binary crate (Cross-Crate),
- Use the crate name
my_app::path prefix to access public items defined inlib.rs.
- Use the crate name
- Local files like
foo.rs,- First, must be added to the module tree using
mod foo;inmain.rs(orlib.rs). - Then access its public items via
foo::path prefix.
- First, must be added to the module tree using
If we load foo.rs via lib.rs, we must add pub mod foo; to lib.rs.
{{< tabs count=3 group="mod-files-in-same-dir" label="Select File" >}} {{< tab label="main.rs" >}}
fn main() {
my_app::greet();
my_app::inline::greet();
my_app::foo::greet();
my_app::foo::inline::greet();
}{{< /tab >}} {{< tab label="lib.rs" >}}
pub mod foo;
pub fn greet() {
println!("Lib");
}
pub mod inline {
pub fn greet() {
println!("Lib:Inline");
}
}{{< /tab >}} {{< tab label="foo.rs" >}}
pub fn greet() {
println!("Foo");
}
pub mod inline {
pub fn greet() {
println!("Foo:Inline");
}
}{{< /tab >}} {{< /tabs >}}
Before Rust 2018:
- Directory-based modules were conventionally defined using a
mod.rsfile. - Submodules of the directory were declared within
mod.rsusingmod(orpub mod) statements. - Ex.
bar/mod.rsacts as the entry point to thebardirectory module.
my_app
βββ Cargo.toml
βββ src
βββ main.rs
βββ bar
βββ mod.rs
βββ qux.rs{{< tabs count=3 label="Select File" >}} {{< tab label="main.rs" >}}
mod bar;
fn main() {
bar::qux::greet();
bar::qux::inline::greet();
}{{< /tab >}} {{< tab label="bar/mod.rs" >}}
pub mod qux;{{< /tab >}} {{< tab label="bar/qux.rs" >}}
pub fn greet() {
println!("Bar:Qux");
}
pub mod inline {
pub fn greet() {
println!("Bar:Qux:Inline");
}
}{{< /tab >}} {{< /tabs >}}
Since Rust 2018:
- Rust added an alternative approach.
- Instead of a
mod.rsfile, we can use a.rsfile with the same name as the directory, placed outside that directory. - Submodules of the directory can be declared within that
.rsfile usingmod(orpub mod) statements. - Ex.
buzz.rsacts as the entry point to thebuzzdirectory module.
my_app
βββ Cargo.toml
βββ src
βββ main.rs
βββ buzz.rs
βββ buzz
βββ qux.rs{{< tabs count=3 label="Select File" >}} {{< tab label="main.rs" >}}
mod buzz;
fn main() {
buzz::qux::greet();
buzz::qux::inline::greet();
}{{< /tab >}} {{< tab label="buzz.rs" >}}
pub mod qux;{{< /tab >}} {{< tab label="buzz/qux.rs" >}}
pub fn greet() {
println!("Buzz:Qux");
}
pub mod inline {
pub fn greet() {
println!("Buzz:Qux:Inline");
}
}{{< /tab >}} {{< /tabs >}}