R
RUSTY YELLOW PAGES

modKeyword


Explanation

mod declares a module in one of two forms.

Written with a body — mod name { ... } — the module's contents sit inline, right there in the braces, in the same file as the declaration.

Written as a bare declaration ending in a semicolon — mod name; — with no body at all, it tells the compiler to load that module's contents from another file instead. The compiler looks for one of two files, relative to the directory of the file containing the mod name; line: name.rs, or name/mod.rs (an older convention, still supported, and still the only option once name itself needs to declare further submodules living in a name/ directory). Both forms declare exactly the same kind of module to the compiler; the only difference is where its contents are written down.

A module declared with either form can be prefixed with pub (see pub) to control its visibility, and can nest further mod declarations, inline or file-loaded, in any combination. See Modules for the mental model — why modules exist, how the tree maps to a public API — and for the file-lookup rule in fuller depth; this page covers the grammar of writing mod itself.

Usage examples

Declaring an inline module

mod shapes {                            // <- `mod` opens an inline module
    pub struct Circle { pub radius: f64 }

    pub fn area(circle: &Circle) -> f64 {
        std::f64::consts::PI * circle.radius * circle.radius
    }
}

let unit_circle = shapes::Circle { radius: 1.0 };
println!("{}", shapes::area(&unit_circle));

Designing a public API

A small metrics library keeps its aggregation and reporting logic in separate files, loaded with mod name;, while re-exporting a short, curated list of public names from the crate root.

// src/lib.rs
mod aggregator;               // <- `mod name;`: loads aggregator.rs as a private module
mod exporter;                 // <- `mod name;`: loads exporter.rs as a private module

pub use aggregator::Counter;  // curated public surface, independent of the file layout
pub use exporter::ConsoleExporter;

// src/aggregator.rs
pub struct Counter {
    count: u64,
}

impl Counter {
    pub fn new() -> Self {
        Counter { count: 0 }
    }

    pub fn increment(&mut self) {
        self.count += 1;
    }
}

// src/exporter.rs
pub struct ConsoleExporter;

impl ConsoleExporter {
    pub fn report(&self, count: u64) {
        println!("count = {count}");
    }
}

The two mod name; declarations keep aggregator and exporter as separate files mirroring the module tree on disk, while the pub use re-exports mean callers depend on crate::Counter and crate::ConsoleExporter rather than on knowing the internal file layout — the curation the API Guidelines' future-proofing chapter recommends for a stable public surface.

Testing

A unit test module uses the inline form of modmod name { ... } — rather than a separate file, since the tests live right alongside the code they exercise.

pub fn celsius_to_fahrenheit(celsius: f64) -> f64 {
    celsius * 9.0 / 5.0 + 32.0
}

#[cfg(test)]
mod tests {                     // <- `mod` here is inline (`mod name { ... }`), not file-loaded
    use super::*;

    #[test]
    fn converts_freezing_point() {
        assert_eq!(celsius_to_fahrenheit(0.0), 32.0);
    }
}

An inline mod tests { ... } compiled only under #[cfg(test)] keeps the tests next to the code they cover instead of in a separate file, the layout the Rust Book's testing chapter uses throughout.

Explanation

Embedded support: Full

mod is a purely compile-time organizational construct with no runtime representation, so both the inline and file-loaded forms work identically in a #![no_std] crate — there's no allocator or OS dependency either way, same as the classic Explanation above. The shape it takes in a HAL crate mirrors the chip it targets: a typical layout splits mod gpio;, mod uart;, mod timer;, and so on, one file per peripheral family, with a generated (often svd2rust-produced) pac module holding the raw register definitions underneath, so a driver module's own code refers to crate::pac::GPIOA rather than duplicating the register layout itself.

Usage examples

A HAL crate's peripheral module layout

// src/lib.rs
#![no_std]

pub mod gpio;   // <- `mod` loads gpio.rs, one file per peripheral family
pub mod uart;
mod pac;        // <- `mod`: the generated register-definition module, not part of the public API

// src/gpio.rs
use crate::pac;

pub struct Pin {
    pin_number: u8,
}

impl Pin {
    pub fn set_high(&mut self) {
        unsafe { pac::GPIOA.odr.modify(|_, w| w.bits(1 << self.pin_number)) }
    }
}