Modules and Paths

Modules

Rust by example - Modules

Modules are Rust's way of organizing code within a crate (a binary or a library).

They help:

  • Group related code: Keep functions, structs, enums, etc., that work together in one place.
  • Control visibility: Decide which parts of your code are public (usable outside the module) and which are private implementation details.
  • Create namespaces: Avoid naming conflicts by putting items into distinct scopes.

To declare a module, use the mod keyword.

A module can be written inline, in the same file than its parent, by using curly brackets: mod <module_name> { ... } or it may be defined in other files, by inserting a semicolon after its declaration: mod <module_name>;.

mod a_file; // Refers to `a_file.rs` or `a_file/mod.rs`.

mod inline_module {
  pub fn some_function() {
    // ...
  }
}

Paths

Paths let you access items (like functions, structs, enums, modules, etc.) within your Rust code, when those items are defined in different modules. There are two main kinds of paths:

Relative paths start from the current module you are writing code in.

/// Inline module declaration.
mod a_module {
    pub fn some_function() {
        // ...
    }
}

fn call_something_in_a_module() {
  // Call `some_function` using a relative path:
  a_module::some_function();

  // Technically, relative paths starts with a `self` keyword,
  // which refers to the current module, but it is very often implied:
  // The above is equivalent to:
  self::a_module::some_function();
}

The super keyword is used to refer to the parent module:

mod a {
  fn in_a() {
    // Call a function in the parent module of `a`:
    super::in_parent_module();
  }
}

fn in_parent_module() {
  // ...
}

Absolute paths start from the root of your crate (the top-level module, usually defined in src/lib.rs or src/main.rs). You use the keyword crate followed by :: to begin an absolute path.

// This absolute path refers to `some_function` located inside `submodule_b`,
// which is inside `module_a`, starting from the crate root.
crate::module_a::submodule_b::some_function();

Absolute paths are rarely seen in practice but useful for disambiguation, when a module name is the same than an external dependency.

Visibility Rules

book-rust-by-example-visibility-rules

In Rust, all items (functions, methods, structs, enums, modules, and constants) are private to their module by default. Items can access other items in the same module, even when private. Use the pub keyword before an item's definition (pub fn, pub struct, etc.) to make it accessible from outside its module.

Items in a parent module can't use the private items inside child modules, but items in child modules can use the items in their ancestor modules.

The following demonstrates the use of (inline) modules, paths to access items within modules, and visibility rules.

// Define a module inline.
// Use the path syntax `module_name::item_name` to access items within.
mod my_module {
    // Items defined inside the module are private by default.
    fn private_function() {
        println!("Called private_function inside my_module.");
    }

    // Use `pub` to make items visible outside the module.
    pub fn public_function() {
        println!("Called public_function inside my_module.");
        private_function(); // One can use / call private items within the same module.
    }

    // Define a nested module.
    // Nested modules are private by default.
    // We make both the module and its inner function public to make the latter
    // accessible.
    pub mod nested_public_module {
        pub fn nested_public() {
            println!("Called nested_public inside nested_module.");
        }
    }

    // Private nested module.
    mod nested_private_module {
        #[allow(dead_code)]
        pub fn nested_public_in_private_module() {}
    }

    // This function calls another in its parent module.
    pub fn call_func_in_parent_module() {
        // `super` in a path refers to the parent module.
        super::private_in_parent_module();
    }
}

// This (private) function can be called from child modules.
fn private_in_parent_module() {
    println!("Called private_in_parent_module.");
}

fn main() {
    // Call a public function within a child module.
    // Note the relative path syntax: `<module_name>::<item_name>`.
    my_module::public_function();

    // my_module::private_function(); // ERROR: private_function is private.

    // Paths can include multiple segments to reach nested modules.
    my_module::nested_public_module::nested_public();

    // Note that both the nested module and its inner function had to be public
    // to be reachable:
    // my_module::nested_private_module::nested_public();
    // ERROR: module `nested_private_module` is private.

    // `call_func_in_parent_module` calls a (private) function
    // in its parent module.
    my_module::call_func_in_parent_module();
}

Split your Code among Several Files and Folders

Each crate has a crate root file (typically main.rs or lib.rs in the src folder). You may write all your code in that file, if it is very short. For non-trivial projects, you will write your code in multiple files and/or folders.

Write mod module_name; to declare a module that has its code in a separate file (note the semicolon at the end and the lack of { }). The compiler then looks for specific files:

  • module_name.rs,
  • module_name/mod.rs (older style),

and insert the contents in the parent module, as if it were an inline module.

You can nest (inline and external-file) modules as you wish.

Note the following:

  • Adding .rs files to your source code folder does not automatically incorporate the code in your crate. You must add an explicit mod statement.
    • Editors like 'VS Code' will not analyze your code or display hints if you forget to do so!
  • The mod statement must be added to the parent file, not to the file that contains the module itself.

Related Topics

  • Package Layout.

References