Modules and Visibility

Module Files

When a module grows beyond a few dozen lines, keeping it inline inside src/main.rs or src/lib.rs makes the root file long and harder to navigate. Rust lets you move any module into its own module file by replacing its inline block mod name { ... } with a semicolon declaration mod name; and placing the module's contents in a matching .rs file on disk.

How mod name; Maps to Files on Disk

When rustc encounters mod network; inside the crate root (src/main.rs or src/lib.rs), it searches for the module's source code in two standard locations relative to src/:

Style File Path for mod network; File Path for Nested mod client; (Inside network) Status
Modern Style (Rust 2018–2024) src/network.rs src/network/client.rs Recommended (every file has a descriptive name in editor tabs)
Older mod.rs Style (Rust 2015) src/network/mod.rs src/network/client.rs Supported, but cannot coexist with src/network.rs for the same module
Recommended Modern File Layout (`src/`):
  src/
  ├── main.rs            (Contains `mod config;` and `mod network;`)
  ├── config.rs          (Contents of module `crate::config`)
  ├── network.rs         (Contents of module `crate::network`; contains `pub mod client;`)
  └── network/
      └── client.rs      (Contents of submodule `crate::network::client`)

Important: Inside src/config.rs, you do not wrap the file's contents in mod config { ... }! The file src/config.rs is already the body of mod config (wrapping its contents in another mod config { ... } would create a nested crate::config::config module!).

Complete Multi-File Example

Suppose a project has three files inside src/:

  1. src/math.rs (defines crate::math and declares submodule pub mod stats;):
// src/math.rs
pub mod stats; // Tells `rustc` to load `src/math/stats.rs`

pub fn square(x: i64) -> i64 {
    x * x
}
  1. src/math/stats.rs (defines crate::math::stats):
// src/math/stats.rs
pub fn sum_of_squares(values: &[i64]) -> i64 {
    // Call `square` from the parent module `math` using `super::`:
    values.iter().map(|&v| super::square(v)).sum()
}
  1. src/main.rs (attaches mod math; to the crate tree and calls both modules):
// src/main.rs
mod math; // Attaches `src/math.rs` to the crate!

fn main() {
    let data = [2, 3, 4];
    println!("square(5) = {}", math::square(5));
    println!("sum_of_squares = {}", math::stats::sum_of_squares(&data));
}
square(5) = 25
sum_of_squares = 29

Crucial Rule: Declare mod name; Exactly Once in the Crate Tree

Unlike #include "math.h" in C or import in Python:

  • mod math; is a module declaration that compiles src/math.rs into the crate. It should appear only once in your entire crate (in the parent module where math belongs, typically src/lib.rs or src/main.rs).
  • Other sibling files do NOT write mod math; to use math! Instead, sibling files refer to crate::math::square (or write use crate::math::square;).
src/main.rs:        `mod math;` + `mod report;`   (Attaches both files to `crate`)
src/report.rs:      `use crate::math::square;`    (Imports from sibling module `crate::math`)

Common Compiler Errors with Module Files

1. Missing Module File (E0583)

If you write mod missing_module; in src/main.rs when neither src/missing_module.rs nor src/missing_module/mod.rs exists on disk, rustc reports E0583 and shows the exact paths it checked:

// ❌ does not compile (when `src/missing_module.rs` does not exist)
mod missing_module;

fn main() {}
error[E0583]: file not found for module `missing_module`
 --> src/main.rs:1:1
  |
1 | mod missing_module;
  | ^^^^^^^^^^^^^^^^^^^
  |
  = help: to create the module `missing_module`, create file "src/missing_module.rs" or "src/missing_module/mod.rs"

error: aborting due to 1 previous error

2. Both name.rs and name/mod.rs Present (E0761)

Having both src/network.rs and src/network/mod.rs in the same directory makes it ambiguous which file defines mod network:

error[E0761]: file for module `network` found at both "src/network.rs" and "src/network/mod.rs"
 --> src/main.rs:1:1
  |
1 | mod network;
  | ^^^^^^^^^^^^
  |
  = help: delete or rename one of them to remove the ambiguity