Explaining Rust’s Modules

Answering once and for all, “Where should we put these files?”

Marc Marburger
Oct 15, 2020 · 6 min read
white chess pieces with one rust-colored pawn ready for play
white chess pieces with one rust-colored pawn ready for play
Photo by Randy Fath on Unsplash

It’s Different

Whatever technology or programming language we use, we tend to structure our ideas and concepts in directories and files, in order to maintain a valuable overview as the project accelerates and expands, to encapsulate knowledge, and to create strong coherence.

Creating these folders in a Rust project, it strikes home fast: Things are very different here. A C-based language programmer tends to import files to get the respective types or namespaces within one’s scope. Playing around with some context help of the IDE or searching the internet, we learn that Rust thinks in modules, not files.

Rust works with modules, as part of a module tree. There is no such thing as file imports.

A Module in Rust

Creating a base project

Our project with src and (build-generated) target folders, along with git and Cargo files

The interesting part, with respect to modules, plays its part in the src subfolder of our project. Looking into the file structure beneath src, we see:

The contents are fairly simple. It’s just the main.rs file, the entry point of our project, containing the main function, usually being created like this:

The src/main.rs (in case of an executable) or src/lib.rs (in case of a library) files are called crate roots.

By contract, main.rs or lib.rs act as the entry point into a package for the Rust compiler rustc.

We can, of course, build and run the project, as should be expected of a raw template. We can set up our base camp right here and talk about how to climb that mountain.

The Plan

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

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

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 glue for auxiliary_building

What’s that :: thing?

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

The full picture

This results in the full code as below:

All involved files in this example

What about submodules?

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:

If the auxiliary.rs file beneath folder src/auxiliary_building were to evolve, you guess right: We can create a new folder, named auxiliary, split code into files, and make available public functionality and symbols in the auxiliary.rs file. An already consuming module might have to adjust the paths accordingly.

Paths, or How to Access a Module From a Module

There are two types of paths: absolute and relative.

Resolving, for example, the generate_energy() function via the path auxiliary_building::auxiliary::generate_energy(), we used an absolute path. They always start from the perspective of the crate root, exactly where our module tree has its origin. This path was resolved from src/main.rs. If we want to use an absolute path from anywhere within our crate, we can use the crate literal.

A relative path, on the other hand, does always take the perspective of the current module, which is the one we are writing our path in. It can be started with the self or super literals.

The literal self will start in the position of the module tree, where the current module is located. In order to navigate up one level in the tree, like in the case of a file system, we use super.

To summarize, there are three literals to start a path: crate (absolute), self, and super (relative).

The following example demonstrates the usage of all three literals:

Demonstrating an absolute and two relative paths, resolving a module from a module

Conclusion

Working through this example, we saw how files can relate to modules in Rust and how Rust’s module tree resolves — or understands — other modules, defined in separated files and folder hierarchies.

Additionally, we looked into paths and how we can resolve dependencies, such as other modules, functionalities, or symbols from within a module or any other point in the project’s module tree.

Better Programming

Advice for programmers.

Sign up for The Best of Better Programming

By Better Programming

A weekly newsletter sent every Friday with the best articles we published that week. Code tutorials, advice, career opportunities, and more! Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Thanks to Zack Shapiro

Marc Marburger

Written by

Freelancer and polyglot software developer. Writer and hobbyist historian.

Better Programming

Advice for programmers.

Marc Marburger

Written by

Freelancer and polyglot software developer. Writer and hobbyist historian.

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store