Lesson 18

Modules & Visibility

Organize your code with modules, control visibility with pub, and use nested namespaces.

Organizing Code with Modules

As programs grow, you need structure. Rust's mod keyword lets you group related code into modules -- think of them as namespaces. In real projects, each module typically lives in its own file or directory. Here in the playground, we define them inline, but the concepts are exactly the same.

Visibility: Private by Default

Everything in a Rust module is private by default. If you want code outside the module to use a function, struct, or constant, you mark it with pub. This is Rust's way of enforcing encapsulation -- you expose only what you intend to, and internal implementation details stay hidden.

Bringing Items Into Scope

To use something from a module, you can write the full path like math::geometry::circle_area(5.0), or you can use use to bring it into the current scope. You can also rename imports with as, and use curly braces to import multiple items at once: use math::{add, subtract}.

In real Rust projects, modules map to the file system: mod math; in main.rs looks for math.rs or math/mod.rs. For now, inline modules let us explore the concept without multiple files.

Try it

// Modules are defined inline here, but in real projects
// each module would be its own file.

mod math {
    // This function is public -- callable from outside the module
    pub fn add(a: f64, b: f64) -> f64 {
        a + b
    }

    pub fn subtract(a: f64, b: f64) -> f64 {
        a - b
    }

    // This function is PRIVATE -- only usable within the math module
    fn _secret_formula(x: f64) -> f64 {
        x * 1.618
    }

    // Nested module!
    pub mod geometry {
        use std::f64::consts::PI;

        pub fn circle_area(radius: f64) -> f64 {
            PI * radius * radius
        }

        pub fn rectangle_area(width: f64, height: f64) -> f64 {
            width * height
        }

        // Private helper -- only geometry can use this
        fn _validate_positive(value: f64) -> bool {
            value > 0.0
        }
    }
}

mod greetings {
    // A public struct, but with controlled field visibility
    pub struct Greeter {
        pub name: String,
        greeting_count: u32,  // private field!
    }

    impl Greeter {
        // Public constructor -- needed because greeting_count is private
        pub fn new(name: &str) -> Greeter {
            Greeter {
                name: name.to_string(),
                greeting_count: 0,
            }
        }

        pub fn greet(&mut self) -> String {
            self.greeting_count += 1;
            format!(
                "Hello from {}! (greeting #{})",
                self.name, self.greeting_count
            )
        }

        pub fn count(&self) -> u32 {
            self.greeting_count
        }
    }
}

// Bring specific items into scope with 'use'
use math::{add, subtract};
use math::geometry::circle_area;
use greetings::Greeter;

fn main() {
    // --- Using module functions ---
    println!("--- Math Module ---");
    println!("  add(3, 4) = {}", add(3.0, 4.0));
    println!("  subtract(10, 3) = {}", subtract(10.0, 3.0));

    // You can also use the full path without 'use'
    println!("  circle_area(5) = {:.2}", circle_area(5.0));
    println!(
        "  rectangle_area(3, 7) = {:.2}",
        math::geometry::rectangle_area(3.0, 7.0)
    );

    // This would fail -- _secret_formula is private:
    // math::_secret_formula(10.0);

    // --- Using a struct from a module ---
    println!("\n--- Greeter Module ---");
    let mut g = Greeter::new("Rustacean");
    println!("  {}", g.greet());
    println!("  {}", g.greet());
    println!("  {}", g.greet());
    println!("  Name: {}", g.name);  // public field
    println!("  Total greetings: {}", g.count());

    // This would fail -- greeting_count is private:
    // println!("{}", g.greeting_count);

    println!("\n--- Visibility Summary ---");
    println!("  pub items: accessible from outside the module");
    println!("  private items: only accessible within the module");
    println!("  pub struct + private fields: use a constructor");
}

Challenge

The _secret_formula function is private. Make it public (rename it to golden_ratio and add pub), then call it from main to see what it returns for the input 10.0.

Previous: String Types

Next: Smart Pointers