Rust Modules and Code Organization

As your Rust program grows, putting all code in one file becomes hard to manage. Modules let you split code into logical groups, control what is visible to other parts of the program, and keep each file focused on one responsibility.

What Is a Module?

A module is a named container for functions, structs, enums, constants, and other modules. Think of it as a folder within your code that groups related items together.

The Office Building Diagram

Crate (whole program)
│
├── mod accounting { ... }    ← Accounting department
├── mod hr { ... }            ← HR department
└── mod engineering {
        mod frontend { ... }  ← Sub-team inside engineering
        mod backend { ... }   ← Sub-team inside engineering
    }

Defining a Module

mod greetings {
    pub fn hello() {
        println!("Hello!");
    }

    pub fn goodbye() {
        println!("Goodbye!");
    }
}

fn main() {
    greetings::hello();
    greetings::goodbye();
}

Output:

Hello!
Goodbye!

The mod keyword defines a module. The double colon :: separates the module name from the item you are accessing.

Visibility with pub

By default, everything inside a module is private — only code within the same module can use it. Add the pub keyword to make an item visible to the outside world.

mod kitchen {
    pub fn serve_food() {
        println!("Food served!");
        prepare_plate();       ← Calling a private function from inside the module is fine
    }

    fn prepare_plate() {      ← Private: only usable inside kitchen
        println!("Plate ready.");
    }
}

fn main() {
    kitchen::serve_food();    ← Works: serve_food is pub
    kitchen::prepare_plate(); ← Compiler error: prepare_plate is private
}

Nested Modules

Modules can contain other modules. Access nested items by chaining names with :::

mod school {
    pub mod math {
        pub fn add(a: i32, b: i32) -> i32 {
            a + b
        }
    }
}

fn main() {
    let result = school::math::add(3, 4);
    println!("{}", result);   ← 7
}

The use Keyword

Typing long module paths every time gets repetitive. Use use to bring a path into scope:

use school::math::add;

fn main() {
    let result = add(3, 4);   ← No need to type school::math::add
    println!("{}", result);
}

You can also import multiple items from the same module:

use std::collections::{HashMap, HashSet};

Modules in Separate Files

For larger programs, move each module into its own file. If you declare mod greetings; in main.rs, Rust looks for either:

  • src/greetings.rs — a single file for the module
  • src/greetings/mod.rs — a folder with a mod.rs file

Example File Structure

src/
├── main.rs
├── greetings.rs     ← Content of mod greetings
└── math.rs          ← Content of mod math

In main.rs:

mod greetings;
mod math;

fn main() {
    greetings::hello();
    let sum = math::add(1, 2);
}

In src/greetings.rs:

pub fn hello() {
    println!("Hello from greetings module!");
}

Paths: Absolute and Relative

crate::school::math::add(1, 2)   ← Absolute path (start from crate root)
school::math::add(1, 2)          ← Relative path (start from current module)
super::helper()                  ← Go up one level to parent module

pub Structs and Enums

Making a struct public does not automatically make its fields public. You must mark each field you want visible with pub individually:

mod shapes {
    pub struct Rectangle {
        pub width: f64,    ← Public field
        pub height: f64,   ← Public field
        area_cache: f64,   ← Private field (hidden from outside)
    }
}

For enums, making the enum pub makes all its variants public automatically.

Quick Reference

mod name { }                ← Define a module
pub fn name() { }           ← Make function visible outside module
use module::item;           ← Bring item into current scope
crate::module::item         ← Absolute path from crate root
super::item                 ← Access parent module
mod name;                   ← Load module from a separate file

Leave a Comment