Rust Modules
As programs grow, keeping everything in one long file gets messy, just like keeping all your papers in one big pile. Modules are Rust's folders for code. They group related functions, structs and enums together, and they decide what the rest of the program is allowed to see.
A module inside one file
You create a module with mod and a name, and put the code in curly brackets. To use something from the module, write the module name, two colons, and the item's name. That is called a path.
mod greetings {
pub fn hello(name: &str) -> String {
format!("Hello, {}!", name)
}
pub fn goodbye(name: &str) -> String {
format!("Goodbye, {}. See you soon!", name)
}
}
fn main() {
println!("{}", greetings::hello("Isha"));
println!("{}", greetings::goodbye("Isha"));
}Hello, Isha! Goodbye, Isha. See you soon!
pub: what others may use
Everything in a module is private by default: only code inside the module can use it. The word pub (for public) opens an item up to the rest of the program. This lets a module keep its helper functions to itself and show only a tidy set of public ones. Try removing a pub above and running the program: the compiler explains that the function is private.
use: shorter paths
Writing long paths again and again gets tiring. use brings a name into scope, so you can write it on its own. You have done this already with use std::io; and use std::collections::HashMap;, which are paths into Rust's standard library.
mod shop {
pub mod pricing {
pub fn with_tax(price: f64) -> f64 {
price * 1.18
}
}
}
use shop::pricing::with_tax;
fn main() {
println!("{:.2}", with_tax(100.0));
println!("{:.2}", with_tax(250.0));
}118.00 295.00
Modules can live inside other modules, as pricing lives inside shop. Each level needs its own pub to be reachable from outside.
Splitting a program into files
When a module gets big, move it to its own file. Write mod geometry; with a semicolon in main.rs, and Rust looks for the code in a file called geometry.rs next to it. In our editor, add the file with the new file button above the file list. The example below opens both files when you press Try it.
mod geometry;
use geometry::Rectangle;
fn main() {
let room = Rectangle::new(4.0, 3.0);
println!("Area: {}", room.area());
println!("Paint needed for the walls: {:.1} litres", geometry::paint_litres(&room, 2.7));
}pub struct Rectangle {
pub width: f64,
pub length: f64,
}
impl Rectangle {
pub fn new(width: f64, length: f64) -> Rectangle {
Rectangle { width, length }
}
pub fn area(&self) -> f64 {
self.width * self.length
}
}
// One litre covers about 10 square metres.
pub fn paint_litres(room: &Rectangle, wall_height: f64) -> f64 {
let walls = 2.0 * (room.width + room.length) * wall_height;
walls / 10.0
}Area: 12 Paint needed for the walls: 3.8 litres
The file geometry.rs is the module geometry, without any mod geometry { } around its contents. Struct fields have their own privacy too: here width and length are pub, so other modules may read them.
Practice exercises
- Make a module
mathswith public functionssquareandcube, and call them frommain. - Add a private helper function to the module and see what happens when
maintries to call it. - Move the
mathsmodule into its own filemaths.rsand use it withmod maths;.