Structs, Enums, and Modules

Explore struct and enum definitions for creating custom data structures. Learn how to organize code using modules.


Rust Modules: Organizing Your Code

What are Modules?

In Rust, modules are a fundamental building block for organizing code. They allow you to:

  • Encapsulate functionality: Bundle related functions, structs, enums, and other items together.
  • Control Visibility: Determine which items are accessible from outside the module and which are kept private.
  • Improve Reusability: Make your code more modular and easier to reuse in different parts of your project or in other projects.
  • Enhance Maintainability: Break down large codebases into smaller, more manageable units, making it easier to understand, modify, and debug.

Defining Modules

You define a module using the mod keyword. Modules can be nested within each other.

Basic Module Definition

 mod my_module {
    // Module contents go here
} 

Nested Modules

 mod parent_module {
    mod child_module {
        // Contents of child_module
    }
} 

Modules in Separate Files

For larger projects, it's best practice to put each module in its own file. Rust automatically looks for a file with the same name as the module (e.g., my_module.rs or my_module/mod.rs) to find the module's contents.

Example structure:

 src/
├── main.rs
└── my_module.rs  // Or my_module/mod.rs 

In src/main.rs:

 mod my_module; // Declare the module

fn main() {
    my_module::hello();
} 

In src/my_module.rs:

 pub fn hello() {
    println!("Hello from my_module!");
} 

Controlling Visibility: `pub`

By default, items defined within a module are private, meaning they can only be accessed from within the module itself. To make an item accessible from outside the module, you must use the pub keyword.

Making Items Public

 mod my_module {
    pub fn public_function() {
        println!("This is a public function.");
    }

    fn private_function() {
        println!("This is a private function.");
    }
}

fn main() {
    my_module::public_function(); // Works
    // my_module::private_function(); // Error: private_function is private
} 

You can make other things public too, like structs, enums, and modules themselves.

 mod my_module {
    pub struct PublicStruct {
        pub field1: i32, // public field
        field2: i32,      // private field
    }

    pub mod public_submodule {
       pub fn another_public_function() {
           println!("Hello from the public submodule!");
       }
    }
}

fn main() {
    let my_struct = my_module::PublicStruct { field1: 10, field2: 20 };
    println!("Public field: {}", my_struct.field1); // works
    //println!("Private field: {}", my_struct.field2); // Doesn't work

    my_module::public_submodule::another_public_function();
} 

Using the `use` Keyword

The use keyword brings items from a module into the current scope, allowing you to refer to them directly without specifying the full module path.

Importing Items

 mod my_module {
    pub fn greet(name: &str) {
        println!("Hello, {}!", name);
    }
}

use my_module::greet; // Import the greet function

fn main() {
    greet("Alice"); // Use the function directly
} 

Renaming Imports

You can rename imports using the as keyword.

 mod my_module {
    pub fn greet(name: &str) {
        println!("Hello, {}!", name);
    }
}

use my_module::greet as say_hello; // Rename greet to say_hello

fn main() {
    say_hello("Bob"); // Use the renamed function
} 

Importing Multiple Items

You can import multiple items at once using curly braces.

 mod my_module {
    pub fn function1() { println!("Function 1"); }
    pub fn function2() { println!("Function 2"); }
}

use my_module::{function1, function2};

fn main() {
    function1();
    function2();
} 

Glob Imports (`use my_module::*`)

The wildcard * can be used to import all public items from a module. However, this is often discouraged as it can make code less explicit and potentially lead to naming conflicts. Use with caution.

 mod my_module {
    pub fn function1() { println!("Function 1"); }
    pub fn function2() { println!("Function 2"); }
}

use my_module::*; // Import everything public from my_module

fn main() {
    function1();
    function2();
} 

Summary

Modules are essential for writing well-structured, maintainable Rust code. They allow you to organize your code into logical units, control visibility, and reuse code effectively. By mastering modules, you can build larger and more complex applications with confidence.