Learning Rust 🦀: 15 - How you can organize your Rust code with "Modules"
Tags: learning, rust, programming, beginners
Published: 2023-10-01 13:59:08

In this article we will see how the Rust's Module system works and how we can group related code together to enhance our project's maintainability. Let's jump in!

>#### ⚠️ Remember!
You can find all the code snippets for this series in its [accompanying repo](https://github.com/FadyGrAb/rust-learning-series)  
If you don't want to install Rust locally, you can play with all the code of this series in the official [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021) that can be found on its official page. That said, the playground may not be suitable for this topic.

> ⚠️⚠️ The articles in this series are loosely following the contents of "The Rust Programming Language, 2nd Edition" by Steve Klabnik and Carol Nichols in a way that reflects my understanding from a Python developer's perspective.

⭐ I try to publish a new article every week (maybe more if the Rust gods 🙌 are generous 😁) so stay tuned 😉. I'll be posting "new articles updates" on my [LinkedIn](https://www.linkedin.com/in/fady-abdelmassih) and [Twitter](https://twitter.com/fadyGrAb).

## Table of Contents:
- [Defining Modules](#defining-modules)
- [Using Paths](#using-paths)
- [The `pub` keyword](#the-raw-pub-endraw-keyword)
- [Structs and Enums inside a module](#structs-and-enums-inside-a-module)
- [The `super` and `self` keywords](#the-raw-super-endraw-and-raw-self-endraw-keywords)
- [Scope management with the `use` keyword](#scope-management-with-the-raw-use-endraw-keyword)
- [Using external packages](#using-external-packages)
- [Nested paths and globing](#nested-paths-and-globing)
- [Separate your modules in different files](#separate-your-modules-in-different-files)

### Defining Modules:
What is a module in Rust? A module in Rust is just a grouping of related code together. We can define all of our modules in a single file or separate them into different files, which proves very handy if our project code base is large.  

But before I show you how to define a module in Rust, I just want to demonstrate how a Rust project is structured using `cargo`. When we type `cargo new <package name>` into our terminal, cargo makes a "package" structure. A package contains "crates" which are in the `src` directory. Crates can be either a library crate or a binary crate. The difference between the two is that a library crate doesnt contain a `main` function and it just provides some functionality to the binary crate(s) in the same package or when used as an external crate.  
A package can contain only one library crate and one or more binary crates.
> So far, all the packages we have created contain only one binary crate.

In the `src` directory, the binary crate root is the `main.rs` and the library crate is the `lib.rs`. The root crate will have the same name as the package's. If our package contains binary crates other than the one in `main.rs`, we put them in `src/bin/` directory and they will be compiled into executables alongside our default binary crate.
> If we typed `cargo new building`, it will create a "building" package and the root binary crate (written in `scr/main.rs`) will be named "building" too. If we wrote a library crate (in `src/lib.rs`), it will also have the same name - building - as the package.

Now that we've got this introduction out of our way, let's see how we can define a module:
```rust
mod my_module {
    // module contents
}
```
That's it! 🙂  

To better demonstrate Rust's module system, let's assume that we are building a digital "hotel" in Rust and we will use the modules system to organize similar code together.  
Type `cargo new hotel` and create a `lib.rs` in the `src` directory.
> We can type `cargo new hotel --lib` and this will create a package `hotel` with only one library crate.

Next, put the following module in our recently created `lib.rs` file. 
```rust
mod reception {
    mod booking {
        fn book_room() {
            println!("Booking room!")
        }
        fn cancel_booking() {}
    }

    mod guests_management {
        fn guest_checkin() {
            println!("Checking in!")
        }
        fn guest_checkout() {}
        fn receive_guest_request() {}
    }
}

mod facilities {
    mod house_keeping {
        fn clean_room() {}
        fn deliver_meal() {}
    }

    mod maintenance {
        fn pool_maintenance() {}
        fn electrical_maintenance() {}
        fn building_maintenance() {}
    }

    mod restaurants {
        fn prepare_tables() {
            println!("Preparing table")
        }
        fn make_meal() {
            println!("Making meal")
        }
    }
}
```
A module in Rust can contain other items such as functions, structs, enums, constants, traits, and other modules. Here we have two main modules, `reception` and `facilities` both containing "child" modules that contain function.

### Using Paths:
To access an item inside a module we use a "file system-like" path which can be either absolute or relative. To demonstrate that, write the following function in `src/lib.rs` outside of any module.
```rust
pub fn go_on_vacation() {
    crate::reception::booking::book_room();         // Absolute path
    reception::guests_management::guest_checkin();  // Relative path
}
```
This function calls two other function from the `reception` module.  It will be a lot easier to understand the module paths if we imagined our `lib.rs` as the following file system:
```
crate
    ├── reception
    |       ├── booking
    |       |       ├── book_room
    |       |       └── cancel_booking
    |       └── guests_management
    |               ├── guest_checkin
    |               └── guest_checkout
    └── facilities
            ├── house_keeping
            |       ├── clean_room
            |       └── deliver_meal
            ├── maintenance
            |       ├── pool_maintenance
            |       ├── electrical_maintenance
            |       └── building_maintenance
            └── restaurants
                    ├── prepare_tables
                    └── make_meal
```
Where the `crate` keyword represents the root crate or the "/" (root) in a Linux file system.  
In the first call, we used an absolute path where we started with the root crate (`crate` keyword) then all the way to the `book_room` function. In the second call, we use a relative path as both the `go_on_vacation` function and the `reception` module are on the same level (children of the root crate), we can refer to the `reception` module from the `go_on_vacation` function like we did.  
Unfortunately, if we tried to run this code, it wouldn't compile! The compilier will complain about `booking` and `guests_management` modules being private!

### The `pub` keyword:
In order to solve the problem we saw in the previous section, we can use the `pub` (for public) keyword. Using `pub` before `mod` will make the module public. Armed with our new found knowledge, let's add the `pub` keyword as follows:
```rust
mod reception {
    pub mod booking {
        // snip
    }

    pub mod guests_management {
        // snip
    }
}
```
If we run the code now, the compiler will complain, again!  
This time, it will complain about `book_rook` and `guest_checkin` functions being private. This highlights an important rule about the `pub` keyword, *making a module public won't make its children public!* and you will have to explicitly choose which components for public access as they are private by default. Let's do the same for those functions as we did with the modules:
```rust
mod reception {
    pub mod booking {
        pub fn book_room() {
            println!("Booking room!")
        }
        // snip
    }

    pub mod guests_management {
        pub fn guest_checkin() {
            println!("Checking in!")
        }
        // snip
    }
}
```
Now, it will be compiled! But wait! we didn't put `pub` before `reception` module, how we could access it?  
As the `reception` module and the `go_on_vacation` function are both on the same level in the "modules system" (the root crate), they can see each other.
> Items that are on the same level are called "siblings" while items that are one level down are called "children" and the items that are one level up are called "parents". 

### Structs and Enums inside a module:
As I've mentioned before, modules can contain *Structs* and *Enums* and both of them (like functions) can accept the `pub` keyword but with a subtle difference! First, let's add a `Room` struct to the `reception` module.
```rust
mod reception {
    // snip
    pub mod guests_management {
        // snip
        pub fn get_room_number() -> i8 {
            10
        }
    }
    #[derive(Debug)]
    pub struct Room {
        pub view: String,
        pub beds_count: i8,
        number: i8,
    }
    impl Room {
        pub fn new(view: &str, beds: i8) -> Room {
            Room {
                view: String::from(view),
                beds_count: beds,
                number: guests_management::get_room_number(),
            }
        }
    }
}
```
And add the following in `src/main.rs`:
```rust
use hotel::guests;
fn main() {
    hotel::go_on_vacation();
}
```
Okay! There is a lot going on here, so let's break it down! 😉
Let's start first with the Struct definition. One thing to notice is that both the struct and some of its elements have the `pub` keyword. For structs if you are going to make the public, in addition to the `pub` before the struct definition, you have to explicitly set which elements are public as the default behavior for them is to be private. Here we have created a `Room` struct that has two public elements, `view` and `beds_count`, and one private element, `number`. But this presents a problem! As the `number` property is private, how can it be set??  
We solve that by creating an implementation for this struct including a `new` function that acts as a factory function for new Rooms. This function sets the `view` and `beds_number` according to the guest's request and sets the `number` using a public function, `get_room_number`, inside the sibling `guests_management` module.
> The implementation of the `get_room_number` function is really silly! It just returns the number `10` for every request! A better function would check the rooms in a database for example and return a vacant one. But this one here will do the trick.

Add the following code to the `go_on_vacation` function and run it:
```rust
pub fn go_on_vacation() {
    // snip

    let my_room = reception::Room::new("Sea view", 2);

    println!(
        "My room's vew is {} and my room number is {}. My room: {:?}",
        my_room.view, my_room.number, my_room
    );
}
```
As expected, it will produce a compilation error as follows:
```
error[E0616]: field `number` of struct `Room` is private
   --> src/lib.rs:100:31
    |
100 |         my_room.view, my_room.number, my_room
    |                               ^^^^^^ private field
```
We are not allowed to access "private" fields of a struct even if the struct itself is "public". If we ommited the erroneous code and replaced it with:
```rust
    println!("My room's vew is {}. My room: {:?}", my_room.view, my_room);
``` 
It will be compiled and we will be able to see that the `number` field of the `Room` struct is set to `10`
```
My room's vew is Sea view. My room: Room { view: "Sea view", beds_count: 2, number: 10 }
```
For Enums, however, if we set it to public, all of its variants will be public too without the need to explicitly set their access to public which makes sense as all the variants of an enum are checked exhaustively so we can't choose a subset to make them public. Add the following enumb to the `restaurant` module inside the `facilities` module:
```rust
mod facilities {
    // snip
    pub mod restaurants {
       // snip
        #[derive(Debug)]
        pub enum Meal {
            Breakfast,
            Brunch,
            Lunch,
            Diner,
        }
    }
}
```
To test it, let's add the following new module to `src/lib.rs`:
```rust
pub mod guests {
    fn book_a_room() {}
    fn go_to_beach() {}
    fn go_to_pool() {}
    pub fn eat_meal() {
        let my_meal = crate::facilities::restaurants::Meal::Diner;
        println!("Eating {my_meal:?}");
    }
    fn end_vacation() {
        println!("Bye bye!");
    }
}
```
And add the following to the main function in `src/main.rs`:
```rust
fn main() {
    // snip
    hotel::guests::eat_meal();
}
```
If we try to run it, we will get the following result as expected
```
Eating Diner
```
### The `super` and `self` keywords:
`super` and `self` are very useful keywords when used in Rust's module system paths. `super` refers to the direct parent of the module that is calling it and `self` refers to the same module that is calling it. 
> The use of `self` will become evidence in the "nested paths" section.

To demonstrate their use, add this code to the `book_a_room` function in the `guest` module:
```rust
    fn book_a_room() {
        super::reception::booking::book_room();
        self::go_to_beach();
    }
```
Here, the `super` expression is referring to the root crate as its the `guest` module's direct parent. Then it accesses the `reception` which is accessible from the root level all the way down to the `book_room` function.  
In the expression with the `self`, it calls the `go_to_beach` function from the same `guest` module.

### Scope management with the `use` keyword:
As we saw, we can use the module system paths to call functions and use elements inside modules provided that they are public. But sometimes the path can be very long! One way to overcome this is by using the `use` keyword as it brings to scope the element at its end and we can use it directly afterwards. Let's try it by making the following changes to the `guests` module:
```rust
pub mod guests {
    use super::reception::guests_management::guest_checkout;
    use crate::facilities::restaurants;

   // snip

    pub fn eat_meal() {
        restaurants::prepare_tables();
        restaurants::make_meal();
        // snip
    }
    fn end_vacation() {
        guest_checkout();
        println!("Bye bye!");
    }
}
```
Here we are bringing both the `restaurants` module and the `guest_checkout` function to scope using the `use` keyword. Notice that in the `eat_meal` function, we are writing `restaurants` directly without its parents as we already brought it into scope. And in the `end_vacation` function, we are also using the `guest_checkout` directly as -similar to the `restaurants` module- we brought it into scope. 
> There is a convention to use the path of the parent module if we are bringing in functions. That way, our code will be more readable as we know the source module of the function used. On the other hand, if we are bringing in structs or enums, we should use the full path.

### Using external packages:
Up until this moment, we've used our own crates. To use external crates, we use the `cargo.toml` file in our project's root like we did [here](https://dev.to/fadygrab/learning-rust-05-your-first-rust-game-guess-the-number-25dn#using-external-crates) in our guessing game. We have to specify the crate name and version under the `dependencies` in the toml file:
```
rand = "0.8.5"
```
Then we can use the `use` keyword of the path to use the functionality provided by this crate.

### Nested paths and globing:
Nested paths is a way to reduce the clutter when importing several elements from the same module. Consider this example when we try to import the `make_meal` and `prepare_tables` functions from the `restaurants` module:
```rust
use hotel::facilities::restaurants::make_meal;
use hotel::facilities::restaurants::prepare_tables;
```
The parents for both functions are the same, so we can use nested paths to reduce the preceding into this:
```rust
use hotel::facilities::restaurants::{make_meal, prepare_tables};
```
And what about the following situation where we want to import the module and one of its elements?:
```rust
use hotel::facilities::restaurants;
use hotel::facilities::restaurants::prepare_tables;
```
We simply use the `self` keyword which we talked about earlier:
```rust
use hotel::facilities::restaurants::{self, prepare_tables}; 
```
And we can use "globing" using "*" to bring "everything" (that is public) into scope from a module:
```rust
use hotel::facilities::restaurants::*
```
This will bring the `prepare_tables` and `make_meal` functions alongside with the `Meal` enum into scope.

### Separate your modules in different files:
Up to this moment, our `src/lib.rs` contains all of our modules which is acceptable with a small code base. But what happens when our project grows bigger? One file containing all of our modules sounds like a bad idea and will decrease the code maintainability!  
Luckily, we can split our modules into separate files. The rules are simple:
1. Write the module contents in a separate file inside `src` directory.
2. Use `mod <module name>` in its parent module (or binary crate) where <modul name> is the same as its file name.
3. If the module contains other submodules, create a directory at the same module file level and follow point 1 and 2.

Let's demonstrate that with an example. Create a new package typing `cargo new hotel_different_files`. Next, create `src/lib.rs` and add `pub mod reception;` to it. After that, create a new file `scr/reception.rs` and the following to it:
```rust
pub mod booking;
pub mod guests_management;

#[derive(Debug)]
pub struct Room {
    view: String,
    beds_count: i8,
    number: i8,
}
impl Room {
    pub fn get_room(view: &str, beds: i8) -> Room {
        Room {
            view: String::from(view),
            beds_count: beds,
            number: guests_management::get_room_number(),
        }
    }
}
```
Here, we have referred to the `booking` and `guests_management` submodules and kept the Room struct and its implementation the same as before.  
As the `reception` module contains submodules, we will create the `src/reception/` directory which contains both submodules as `src/reception/booking.rs` and `src/reception/guests_management.rs` which will contain the following respectively:
```rust
// booking.rs
pub fn book_room() {
    println!("Booking room!")
}
pub fn cancel_booking() {}
```
```rust
// guests_management.rs
pub fn guest_checkin() {
    println!("Checking in!")
}
pub fn guest_checkout() {}
fn receive_guest_request() {}

pub fn get_room_number() -> i8 {
    10
}
```
And finally, to test everything out, add the following to `src/main.rs`:
```rust
use hotel_different_files::reception;

fn main() {
    let my_room = reception::Room::get_room("pool view", 1);
    println!("My room is {my_room:?}");
}
```
And it will print the `Room` struct exactly as our "one-file" implementation!

Few! 😮‍💨, this was a long but interesting one! Buy now, we should have a basic understanding of Rust's Module system. In the next article, We will start exploring Rust's common collections! See you then 👋