Rust’s Module System
mod
and pub mod
Rust gives you a good API for modularizing your code. The mod
keyword, simply put.
mod my_module {
fn private_function() {
println!("This is a private function");
}
pub fn public_function() {
println!("This is a public function");
}
}
// call the function
my_module::public_function();
Pretty straightforward. Functions are private by default unless explicitly marked as pub
.
Nested modules follow the same behavior, which is why you see the pub mod
combination of keywords.
mod my_module {
mod nested_private_functions {
fn nested_private_functions () {}
}
pub mod my_api {
// ..public apis
fn get_stuff() {
// do stuff
}
}
}
// good
my_module::my_api::get_stuff();
// will fail to compile
my_module::nested_private_functions::nested_private_functions();
Inline modules vs. defined in a file.
Modules can be created by explicitly writing code inside of a mod { ... }
block, as the current examples thus far have shown. This is called an inline module.
Modules can also be created by moving the code into a separate file, then using the same mod
declaration in the file you want to invoke the module from.
// cars.rs
pub struct Car {
make: String,
gas_level: i32,
mileage: i32,
}
// lib.rs
mod cars; // <- pulls the code from cars.rs into scope for the current file, under the "cars" module
// inline module
pub mod my_module {
fn do_stuff () { }
}
fn main() {
my_module::do_stuff();
let volvo = cars::Car::new(String::from("volvo"), 100); // use the code from the module
}
Nested modules
As mentioned earlier, Rust allows nested modules. These can be inline nested modules, or they can also be nested based on the file structure. You can access the parent module using the super
keyword:
mod outer {
pub mod inner {
pub fn call_super() {
super::parent_function(); // Call a function from the parent module
}
}
pub fn parent_function() {
println!("Called from parent module");
}
}
One thing to note: in Rust, modules must be declared by the parent module.
use
keyword
Looking at rust code, you will also see this use
keyword getting called extensively. This keyword allows us to bring items from a module into scope, so to avoid writing long paths repeatedly.
mod cars;
use crate::cars::Car;
fn main() {
let volvo = Car::new(String::from("volvo"), 100); // use the code from the module
}
You can also use this strategy to bring modules from external dependencies into scope as well:
use crate::cars::Car; // local module
extern crate serde; // external module
let json = r#"{"make": "Volvo"}"#;
let parsed: serde_json::Value = serde_json::from_str(json).unwrap();
let volvo = Car::new(...);
Please note: extern crate
is usually not necessary since Rust 2018, usually we can just say crate
. Some macros still require extern
.
crate
vs. mod
This can get a bit confusing since a “crate” sounds like it would be a package, or a module. In fact, a “crate” in rust is distinct from module.
crate
is the top-level compilation unit in Rust. A Rust program is a crate.module
is a way to organize and structure code within a crate
mod.rs
Rust does not actually explicitly use files for modules like JS or python. A module is not necessarily 1:1 with a file.
mod.rs
is an optional convention in Rust used to define a module in a directory without needing to specify an additional file name. When Rust encounters a directory that is declared as a module, it looks for mod.rs
in that directory as the entry point for the module’s implementation. This approach allows organizing related code and modules within a directory structure while keeping the code modular and maintainable.
So, in other words, you can do this:
my_project/
│
├── src/
│ ├── main.rs
│ ├── lib.rs
│ ├── my_module/
│ │ ├── mod.rs
│ │ ├── sub_module.rs
│
Then in main.rs
, use stuff from my_module::sub_module
Summary
So, to conclude, some cliffnotes:
mod
defines a module.- Modules can be inline or in separate files.
- Public items are exposed with
pub
. - Use
use
to bring items into scope. - Paths can be absolute or relative using
crate
,self
, orsuper
, and can also refer to external dependencies.