Understanding Rust Modules
Coming from JavaScript world, understanding Rust modules was a bit challenging. This document aims to describe the Rust modules system via common examples.
There are 3 important keywords (source):
mod
declares a modulepub
exposes an item by a single leveluse
pulls things in from an absolute path to the current scope
I recommend creating experiments
package if you want to try these examples and run them via:
cargo run --bin experiments
Sources:
- https://doc.rust-lang.org/reference/items/modules.html
- https://doc.rust-lang.org/reference/items/use-declarations.html
- https://doc.rust-lang.org/reference/visibility-and-privacy.html
- https://dev.to/hertz4/rust-module-essentials-12oi
- https://stackoverflow.com/q/28010796/3135248
In-place (in-file) modulesβ
βββ Cargo.toml
βββ src
βββ main.rs
This way you can define the file inside one file:
mod my_module {
pub fn test() {
println!("OK π");
}
}
fn main() {
my_module::test();
}
Module in a separate fileβ
The module my_module
can be moved into separate file like so:
βββ Cargo.toml
βββ src
βββ main.rs
βββ my_module.rs
In this case main
declares the module without the body:
mod my_module;
fn main() {
my_module::test();
}
Our module lives in a separate file without the mod
declaration:
pub fn test() {
println!("OK π");
}
What happens if we move the module into separate file including the module declaration like in the following example?
pub mod my_module {
pub fn test() {
println!("OK π");
}
}
First, we have to declare the module public (see the pub
keyword). Secondly, the module would have to be used like so:
mod whatever;
fn main() {
whatever::my_module::test();
}
Module in a separate directoryβ
βββ Cargo.toml
βββ src
βββ main.rs
βββ my_module
βββ mod.rs
It's possible to decompose the first example a bit differently by introducing a new directory with special mod.rs
file:
mod my_module;
fn main() {
my_module::test();
}
And the actual module:
pub fn test() {
println!("OK π");
}
Re-exporting submoduleβ
So far, we used keywords mod
to declare the module and pub
to make it visible. Let's try to play around with use
keywords. Imagine the following structure:
βββ Cargo.toml
βββ src
βββ main.rs
βββ my_module
βββ mod.rs
βββ my_submodule.rs
File main.rs
would use the submodule like so:
mod my_module;
fn main() {
my_module::my_submodule::test();
}
File my_module/mod.rs
simply re-exports the submodule:
pub mod my_submodule;
And finally the submodule:
pub fn test() {
println!("OK π");
}
We could modify it in several ways. For example main.rs
could import the test
method directly like so:
mod my_module;
use my_module::my_submodule::test;
fn main() {
test();
}
What if we would like to hide the fact that there is a submodule and use my_module::test
directly like in the following example?
mod my_module;
fn main() {
my_module::test();
}
This is easily achievable by re-exporting the submodule via pub use
keywords:
mod my_submodule;
pub use my_submodule::test;
Reexporting with self
β
pub use self::implementation::api;
mod implementation {
pub mod api {
pub fn f() {}
}
}
Any external crate referencing implementation::api::f
would receive a privacy violation, while the path api::f
would be allowed.
When re-exporting a private item, it can be thought of as allowing the "privacy chain" being short-circuited through the reexport instead of passing through the namespace hierarchy as it normally would.
Source: https://doc.rust-lang.org/reference/visibility-and-privacy.html