Modules and Paths

Structure your Code into Modules

Rust by example - Modules

Modules are Rust's way of organizing code within a crate (a binary or a library). Modules are containers that 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.

Modules form a tree that originates in the "crate root file" (usually src/lib.rs for a library crate or src/main.rs for a binary crate). In the crate root file, you can declare modules. In turn, in each module, you can nest other modules, and so on.

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 a separate file or folder, by inserting a semicolon after its declaration: mod <module_name>;:

// Module in separate file: `a_file.rs` or `a_file/mod.rs` - see below.
mod a_file;

// Inline module.
mod inline_module {
    // The module's items are written within the brackets.
    fn some_function() {
      // ...
    }
}

Note that modules (and items within) are private by default. Review visibility rules.

Split your Code into Several Files

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 organized into multiple 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 effectively insert the contents in the parent module, as if it were an inline module.

You can nest (inline or external-file) modules as you wish:

// Inline module.
mod a {

    // The module code is in `a/b.rs` or to `a/b/mod.rs`.
    // `b.rs` or `mod.rs` can in turn contain modules.
    mod b;

    // Nested inline module.
    mod c {
    // ...
    }
}

The "older style" conveniently groups all files for a module and its submodules into one folder, but results in a proliferation of mod.rs files.

Note the following:

  • Adding .rs files to your source code folder does not automatically incorporate the code in your crate. You must add explicit mod statements. Editors like 'VS Code' will not analyze your code or display hints while typing 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.
  • Modules (and items within) are private by default. Use the pub keyword - see visibility rules.
  • It is possible (but confusing) to override the name and path of the file where a module is stored, using the path attribute⮳.

Access Items Within Modules via Paths

Paths let you use items (like functions, structs, enums, modules, etc.) within your Rust code, when those items are defined in different modules. Paths consist of a sequence of one or more segments separated by ::, where each segment is an item (often a module), an alias, or a keyword like super, self, or crate:

a_module                          // Path to a module.
a_module::a_function              // Path to a function in a module.
a_module::Struct1                 // Path to a struct in a module. Also works for enums, constants, etc.

a_module::nested_module           // Path to a nested module.
a_module::nested_module::b_function // Path to a function in a nested module.

super                             // Path to the parent module.
super::brother_module             // Path to another module declared in the parent module.

crate::first_level::second_level  // Path to a nested module, starting from the crate root.

Note that visibility rules apply. A path is valid only if all of its segments are accessible from the location of use. See the section below. Aliases are covered in use keyword chapter.

Going into more details, there are two main kinds of paths: relative and absolute 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();

    // Relative paths can also start with a `self` keyword,
    // which refers to the current module, but it is very often elided:
    // The above is equivalent to:
    self::a_module::some_function();
}
// The inner function was marked public with `pub`,
// so that it could be accessed. See visibility rules below.

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.

// In the crate root:
mod module_a {
  pub mod submodule_b {
    pub fn some_function() {
      // ...
    }
  }
}

// Elsewhere in the code:
crate::module_a::submodule_b::some_function();

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

Paths can be used to refer to elements within containers other than modules: structs, enums, traits, etc.

a_module::Enum1::Variant1         // Path to a variant in an enum, itself in a module.
TypeName::a_function()            // Path to an associated function within a type (e.g. a struct, an enum).
<ImplType as Trait>::AssocType    // Path to an associated type within a trait.
TypeName::CONSTANT_NAME           // Path to an associated constant within a type's `impl` block.

Use Modules to Hide Implementation Details

Modules provides encapsulation, meaning they hide items within from their parent, unless the items are explicitly made public.

  • Most items, including modules and items within, are private by default.
  • Use the pub keyword to make them public.
  • All items, public or private, can be accessed from within the same module or child modules.
  • Items cannot be accessed from their parent, unless they are public.
  • Items cannot be accessed from another module, unless all items in the path from that module to the item are accessible (public, if traversed in the parent -> child direction; private or public, if traversed in the child -> parent direction or in the same module).

The following example summarizes the visibility rules when it comes to modules. See the Visibility and use keyword chapters for complete details.


// This file is an (implicit) module.

// A private function in this top module.
fn private_in_top_module() {
    println!("Called private_in_top_module.");
}

// Let's define an inner module inline with the `mod` keyword.
// Modules are private by default.
mod my_module {
    // Items defined inside the module are also private by default.
    fn private_function() {
        println!("Called private_function inside my_module.");
    }
    // Use `pub` to make items public.
    pub fn public_function() {
        println!("Called public_function inside my_module.");
    }

    // Define a public nested module with the `pub` keyword.
    // We make both the module and its inner function public.
    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.
    // This is always allowed, even if private.
    pub fn call_func_in_parent_module() {
        // `super` in a path refers to the parent module.
        super::private_in_top_module();
    }
}

fn main() {
    // Items can access other items in the _same_ module, even if private.
    private_in_top_module();

    // In the same way, `my_module` is _private_, but _accessible_ from
    // `main()`. Here, we define an alias;
    use my_module as alias;

    // Private items are inaccessible from a parent module.
    // my_module::private_function(); // ERROR: private_function is private.

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

    // You can also use the alias.
    alias::public_function();

    // 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();
}

Related Topics

  • Package Layout.

References