Module System of Rust

The Rust module system, often called “The Module System,” is a powerful feature for organizing code, managing privacy, and controlling the scope of items like functions, structs, and enums. It helps in keeping projects clean and maintainable as they grow.

The main components of the module system are:

  • Packages: The largest unit, managed by Cargo. A package contains one or more crates and a Cargo.toml file.
  • Crates: The smallest unit of compilation. A crate can be a binary (executable) or a library.
  • Modules: Used to organize code within a crate.
  • Paths: Used to name and access items within modules.

Packages and Crates

  • A Package is a bundle of one or more crates that provides a set of functionality. It’s defined by its Cargo.toml file.
  • A Crate is either a library or a binary. The source file that the compiler starts from (src/main.rs for a binary or src/lib.rs for a library) is called the crate root.

Modules (mod)

Modules let you group related definitions together and control their privacy.

  • You can declare a module with the mod keyword.
  • By default, all items (functions, structs, etc.) in a module are private to that module.
  • You can make an item public by using the pub keyword.
// In src/lib.rs

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant() {
    // Absolute path from crate root
    crate::front_of_house::hosting::add_to_waitlist();

    // Relative path
    front_of_house::hosting::add_to_waitlist();
}

Paths and the use Keyword

A path is how you name an item. The use keyword brings a path into scope, allowing you to use a shorter name.

// Bring the hosting module into scope
use crate::front_of_house::hosting;

// You can also bring a function directly into scope
// use crate::front_of_house::hosting::add_to_waitlist;

pub fn eat_at_restaurant() {
    // Now we can use a shorter path
    hosting::add_to_waitlist();
    
    // If the function was brought into scope directly:
    // add_to_waitlist();
}

Separating Modules into Different Files

As modules grow, you can extract them into their own files to keep your code organized.

If you have a module named front_of_house in src/lib.rs:

// In src/lib.rs
mod front_of_house;

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

The compiler will look for the module’s code in:

  1. src/front_of_house.rs
  2. src/front_of_house/mod.rs

This allows you to build a clean directory structure for your project. For example:

src/
├── lib.rs
└── front_of_house.rs

Or for nested modules:

src/
├── lib.rs
└── front_of_house/
    ├── mod.rs      // Contains `pub mod hosting;`
    └── hosting.rs  // Contains the hosting module's code