Modules#

Source:

src/rust/modules

Rust’s module system organizes code into hierarchical namespaces. Unlike C++ headers, Rust modules are part of the language with explicit visibility rules.

Module Comparison#

C++

Rust

#include

mod / use

namespace

mod

Header files (.h)

Module files (.rs)

public:

pub

private:

(default, no keyword)

using namespace

use

Defining Modules#

C++:

// math.h
namespace math {
  int add(int a, int b);

  namespace utils {
    int square(int x);
  }
}

// math.cpp
namespace math {
  int add(int a, int b) { return a + b; }

  namespace utils {
    int square(int x) { return x * x; }
  }
}

Rust (inline modules):

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

    pub mod utils {
        pub fn square(x: i32) -> i32 {
            x * x
        }
    }
}

fn main() {
    let sum = math::add(1, 2);
    let sq = math::utils::square(3);
}

File-Based Modules#

Rust maps modules to files:

src/
├── main.rs
├── math.rs          # mod math
└── math/
    └── utils.rs     # mod math::utils

src/main.rs:

mod math;  // loads math.rs or math/mod.rs

fn main() {
    let sum = math::add(1, 2);
    let sq = math::utils::square(3);
}

src/math.rs:

pub mod utils;  // loads math/utils.rs

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

src/math/utils.rs:

pub fn square(x: i32) -> i32 {
    x * x
}

Visibility (pub)#

mod outer {
    pub fn public_fn() {}      // visible outside module
    fn private_fn() {}         // only visible in this module

    pub mod inner {
        pub fn inner_public() {}
        fn inner_private() {}

        // pub(super) - visible to parent module
        pub(super) fn parent_only() {}

        // pub(crate) - visible within crate
        pub(crate) fn crate_only() {}
    }
}

fn main() {
    outer::public_fn();           // OK
    // outer::private_fn();       // error: private
    outer::inner::inner_public(); // OK
    // outer::inner::parent_only(); // error: not visible here
}

Struct Field Visibility#

mod shapes {
    pub struct Rectangle {
        pub width: u32,   // public field
        height: u32,      // private field
    }

    impl Rectangle {
        pub fn new(width: u32, height: u32) -> Self {
            Rectangle { width, height }
        }

        pub fn area(&self) -> u32 {
            self.width * self.height
        }
    }
}

fn main() {
    let rect = shapes::Rectangle::new(10, 20);
    println!("{}", rect.width);  // OK
    // println!("{}", rect.height);  // error: private
}

The use Keyword#

C++:

using namespace std;
using std::vector;
namespace fs = std::filesystem;

Rust:

// Import single item
use std::collections::HashMap;

// Import multiple items
use std::collections::{HashMap, HashSet};

// Import all public items (glob)
use std::collections::*;

// Rename import
use std::collections::HashMap as Map;

// Re-export
pub use std::collections::HashMap;

use Paths#

mod foo {
    pub mod bar {
        pub fn baz() {}
    }
}

// Absolute path from crate root
use crate::foo::bar::baz;

// Relative path
use self::foo::bar::baz;

// Parent module
use super::something;

// External crate
use std::io::Read;

Crate Structure#

my_crate/
├── Cargo.toml
└── src/
    ├── lib.rs       # crate root for library
    ├── main.rs      # crate root for binary
    ├── module_a.rs
    └── module_b/
        ├── mod.rs   # module_b root
        └── sub.rs   # module_b::sub

src/lib.rs:

pub mod module_a;
pub mod module_b;

// Re-export for convenient access
pub use module_a::important_fn;

Prelude Pattern#

// src/lib.rs
pub mod prelude {
    pub use crate::module_a::TypeA;
    pub use crate::module_b::TypeB;
    pub use crate::traits::*;
}

// Users can import common items easily:
// use my_crate::prelude::*;

External Crates#

Cargo.toml:

[dependencies]
serde = "1.0"
tokio = { version = "1", features = ["full"] }

src/main.rs:

use serde::{Serialize, Deserialize};
use tokio::fs::File;

#[derive(Serialize, Deserialize)]
struct Config {
    name: String,
}

Conditional Compilation#

// Platform-specific modules
#[cfg(target_os = "linux")]
mod linux;

#[cfg(target_os = "windows")]
mod windows;

// Feature-gated modules
#[cfg(feature = "advanced")]
pub mod advanced;

// Test module
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_something() {
        assert!(true);
    }
}

See Also#