Explaining Rust’s Modules
By: Marc Marburger
Tags: Rust, Software Development, DevOps, Programming, Rustlang

The Plan

We want to build a project that consists of two modules being imported from src/main.rs . These modules will expose public functionality as well as define module-only parts. The project will also demonstrate how modules can use functionality from other modules.

There are two modules: a power plant and a power-consuming tower.

Let’s think of our crate root ( src/main.rs ) as a street. We want to construct two buildings next to each other: a tower building and an auxiliary power building. These two buildings will be our two modules. Our construction site (project) has dependencies between the street and its buildings, the power plant, generating power, and the tower building consuming it.

Adding files and folders to the project

When defining a module that is to be split into folders and files, we create a directory, and, on level with the crate root ( main.rs or lib.rs ), another file. Both carry the module name as directory- and filename. A structure for the module example_module would be:

An exemplary module structure

In our case, creating two buildings, or modules, we have to create two directories and two files. This results in a new folder structure in our project:

The project’s src folder structure

The glue

Of course, we have to also write some code to glue together the mere file system items. A module, represented by a directory, can consist of various files. Each of these files might define new types or symbols in Rust. The only missing part is how to bring the parts together. How to connect the buildings with our street?

Rust relies on a module tree in order to resolve all parts necessary to build.

As covered before, the (crate) root of that tree is our src/main.rs file. So somehow, reaching out from this root, we have to tell Rust there is a (tree-)branch to add. Remembering the module-like named file — created along with the directory — we already have something to plug and play.

The referencing for a module example_module would be like so:

Referencing of example_module

Beneath “Seen as modules,” we see a difference between lines 10 and 11 in that one line has declared pub ³ and the other not. The keyword pub tells Rust’s module system that the respective module is public to a referencing outer module, such as our crate root ( src/main.rs ). Module a is only visible within our exemplary_module , whereas module b is visible to the outside.

Note: You tell Rust to make a module, function, or symbol visible with the keyword pub .

The code

The module tree is obviously put together in structuring directories and their respective module-file and in writing some glue code within these files. The code, beginning in src/auxiliary_building/auxiliary.rs , jumping to src/auxiliary_building.rs , and ending in src/main.rs , would be as follows:

The glue for auxiliary_building

What’s that :: thing?

A double colon separates parts of a path⁴. A path can be considered similar to resolving resources by namespaces and type names. We’ll talk about them later on in this article.

The same procedure has to be performed for our second module, or building within the street, the tower building.

The full picture

The crate root, src/main.rs , will access the tower_building module through mod ⁵ and use ⁶ keywords. The tower_building module will define its parts within the src/tower_building.rs file, and a file within the src/tower_building (we named it tower.rs above) will define some symbols to be used, completing the circle, in src/main.rs .

This results in the full code as below:

All involved files in this example

What about submodules?

If we want to introduce some folder beneath auxiliary_building , say, a plug module , we act as before: create a folder and a file for it. The new structure would be:

The project structure after adding a plug folder and file

Having created the file system part, we have to add some glue for the module system. As the submodule plug is added beneath the auxiliary_building module, we have to adjust the src/auxiliary_building.rs file accordingly. We add the plug module:

After doing so, we can use whatever is created within the src/auxiliary_building/plug folder in our src/main.rs . I created a function in device.rs and other_device.rs in order to demonstrate the availability: