mod and the Filesystem - The Rust Programming Language

## `mod` and the Filesystem

We’ll start our module example by making a new project with Cargo, but instead of creating a binary crate, we’ll make a library crate: a project that other people can pull into their projects as a dependency. For example, the `rand` crate discussed in Chapter 2 is a library crate that we used as a dependency in the guessing game project.

We’ll create a skeleton of a library that provides some general networking functionality; we’ll concentrate on the organization of the modules and functions, but we won’t worry about what code goes in the function bodies. We’ll call our library `communicator`. To create a library, pass the `--lib` option:

    $ cargo new communicator --lib
    $ cd communicator

Notice that Cargo generated src/lib.rs instead of src/main.rs. Inside src/lib.rs we’ll find the following:

Filename: src/lib.rs

    # #![allow(unused_variables)]
    #fn main() {
    #[cfg(test)]
    mod tests {
        #[test]
        fn it_works() {
            assert_eq!(2 + 2, 4);
        }
    }
    #}

Cargo creates an example test to help us get our library started. We’ll look at the `#[]` and `mod tests` syntax in the “Using `super` to Access a Parent Module” section later in this chapter, but for now, leave this code at the bottom of src/lib.rs.

Because we don’t have a src/main.rs file, there’s nothing for Cargo to execute with the `cargo run` command. Therefore, we’ll use the `cargo build` command to compile our library crate’s code.

We’ll look at different options for organizing your library’s code that will be suitable in a variety of situations, depending on the intent of the code.

### Module Definitions

For our `communicator` networking library, we’ll first define a module named `network` that contains the definition of a function called `connect`. Every module definition in Rust starts with the `mod` keyword. Add this code to the beginning of the src/lib.rs file, above the test code:

Filename: src/lib.rs

    # #![allow(unused_variables)]
    #fn main() {
    mod network {
        fn connect() {
        }
    }
    #}

After the `mod` keyword, we put the name of the module, `network`, and then a block of code in curly brackets. Everything inside this block is inside the namespace `network`. In this case, we have a single function, `connect`. If we wanted to call this function from code outside the `network` module, we would need to specify the module and use the namespace syntax `::` like so: `network::connect()`.

We can also have multiple modules, side by side, in the same src/lib.rs file. For example, to also have a `client` module that has a function named `connect`, we can add it as shown in Listing 7-1:

Filename: src/lib.rs

    # #![allow(unused_variables)]
    #fn main() {
    mod network {
        fn connect() {
        }
    }

    mod client {
        fn connect() {
        }
    }
    #}

Listing 7-1: The `network` module and the `client` module defined side by side in src/lib.rs

Now we have a `network::connect` function and a `client::connect` function. These can have completely different functionality, and the function names do not conflict with each other because they’re in different modules.

In this case, because we’re building a library, the file that serves as the entry point for building our library is src/lib.rs. However, in respect to creating modules, there’s nothing special about src/lib.rs. We could also create modules in src/main.rs for a binary crate in the same way as we’re creating modules in src/lib.rs for the library crate. In fact, we can put modules inside of modules, which can be useful as your modules grow to keep related functionality organized together and separate functionality apart. The way you choose to organize your code depends on how you think about the relationship between the parts of your code. For instance, the `client` code and its `connect` function might make more sense to users of our library if they were inside the `network` namespace instead, as in Listing 7-2:

Filename: src/lib.rs

    # #![allow(unused_variables)]
    #fn main() {
    mod network {
        fn connect() {
        }

        mod client {
            fn connect() {
            }
        }
    }
    #}

Listing 7-2: Moving the `client` module inside the `network` module

In your src/lib.rs file, replace the existing `mod network` and `mod client` definitions with the ones in Listing 7-2, which have the `client` module as an inner module of `network`. The functions `network::connect` and `network::client::connect` are both named `connect`, but they don’t conflict with each other because they’re in different namespaces.

In this way, modules form a hierarchy. The contents of src/lib.rs are at the topmost level, and the submodules are at lower levels. Here’s what the organization of our example in Listing 7-1 looks like when thought of as a hierarchy:

    communicator
     ├── network
     └── client

And here’s the hierarchy corresponding to the example in Listing 7-2:

    communicator
     └── network
         └── client

The hierarchy shows that in Listing 7-2, `client` is a child of the `network` module rather than a sibling. More complicated projects can have many modules, and they’ll need to be organized logically in order for you to keep track of them. What “logically” means in your project is up to you and depends on how you and your library’s users think about your project’s domain. Use the techniques shown here to create side-by-side modules and nested modules in whatever structure you would like.

### Moving Modules to Other Files

Modules form a hierarchical structure, much like another structure in computing that you’re used to: filesystems! We can use Rust’s module system along with multiple files to split up Rust projects so not everything lives in src/lib.rs or src/main.rs. For this example, let’s start with the code in Listing 7-3:

Filename: src/lib.rs

    # #![allow(unused_variables)]
    #fn main() {
    mod client {
        fn connect() {
        }
    }

    mod network {
        fn connect() {
        }

        mod server {
            fn connect() {
            }
        }
    }
    #}

Listing 7-3: Three modules, `client`, `network`, and `network::server`, all defined in src/lib.rs

The file src/lib.rs has this module hierarchy:

    communicator
     ├── client
     └── network
         └── server

If these modules had many functions, and those functions were becoming lengthy, it would be difficult to scroll through this file to find the code we wanted to work with. Because the functions are nested inside one or more `mod` blocks, the lines of code inside the functions will start getting lengthy as well. These would be good reasons to separate the `client`, `network`, and `server` modules from src/lib.rs and place them into their own files.

First, let’s replace the `client` module code with only the declaration of the `client` module so that src/lib.rs looks like code shown in Listing 7-4:

Filename: src/lib.rs

    mod client;

    mod network {
        fn connect() {
        }

        mod server {
            fn connect() {
            }
        }
    }

Listing 7-4: Extracting the contents of the `client` module but leaving the declaration in src/lib.rs

We’re still declaring the `client` module here, but by replacing the block with a semicolon, we’re telling Rust to look in another location for the code defined within the scope of the `client` module. In other words, the line `mod client;` means this:

    mod client {
        // contents of client.rs
    }

Now we need to create the external file with that module name. Create a client.rs file in your src/ directory and open it. Then enter the following, which is the `connect` function in the `client` module that we removed in the previous step:

Filename: src/client.rs

    # #![allow(unused_variables)]
    #fn main() {
    fn connect() {
    }
    #}

Note that we don’t need a `mod` declaration in this file because we already declared the `client` module with `mod` in src/lib.rs. This file just provides the contents of the `client` module. If we put a `mod client` here, we’d be giving the `client` module its own submodule named `client`!

Rust only knows to look in src/lib.rs by default. If we want to add more files to our project, we need to tell Rust in src/lib.rs to look in other files; this is why `mod client` needs to be defined in src/lib.rs and can’t be defined in src/client.rs.

Now the project should compile successfully, although you’ll get a few warnings. Remember to use `cargo build` instead of `cargo run` because we have a library crate rather than a binary crate:

    $ cargo build
       Compiling communicator v0.1.0 (file:///projects/communicator)
    warning: function is never used: `connect`
     --> src/client.rs:1:1
      |
    1 | / fn connect() {
    2 | | }
      | |_^
      |
      = note: #[warn(dead_code)] on by default

    warning: function is never used: `connect`
     --> src/lib.rs:4:5
      |
    4 | /     fn connect() {
    5 | |     }
      | |_____^

    warning: function is never used: `connect`
     --> src/lib.rs:8:9
      |
    8 | /         fn connect() {
    9 | |         }
      | |_________^

These warnings tell us that we have functions that are never used. Don’t worry about these warnings for now; we’ll address them later in this chapter in the “Controlling Visibility with `pub`” section. The good news is that they’re just warnings; our project built successfully!

Next, let’s extract the `network` module into its own file using the same pattern. In src/lib.rs, delete the body of the `network` module and add a semicolon to the declaration, like so:

Filename: src/lib.rs

    mod client;

    mod network;

Then create a new src/network.rs file and enter the following:

Filename: src/network.rs

    # #![allow(unused_variables)]
    #fn main() {
    fn connect() {
    }

    mod server {
        fn connect() {
        }
    }
    #}

Notice that we still have a `mod` declaration within this module file; this is because we still want `server` to be a submodule of `network`.

Run `cargo build` again. Success! We have one more module to extract: `server`. Because it’s a submodule—that is, a module within a module—our current tactic of extracting a module into a file named after that module won’t work. We’ll try anyway so you can see the error. First, change src/network.rs to have `mod server;` instead of the `server` module’s contents:

Filename: src/network.rs

    fn connect() {
    }

    mod server;

Then create a src/server.rs file and enter the contents of the `server` module that we extracted:

Filename: src/server.rs

    # #![allow(unused_variables)]
    #fn main() {
    fn connect() {
    }
    #}

When we try to `cargo build`, we’ll get the error shown in Listing 7-5:

    $ cargo build
       Compiling communicator v0.1.0 (file:///projects/communicator)
    error: cannot declare a new module at this location
     --> src/network.rs:4:5
      |
    4 | mod server;
      |     ^^^^^^
      |
    note: maybe move this module `src/network.rs` to its own directory via `src/network/mod.rs`
     --> src/network.rs:4:5
      |
    4 | mod server;
      |     ^^^^^^
    note: ... or maybe `use` the module `server` instead of possibly redeclaring it
     --> src/network.rs:4:5
      |
    4 | mod server;
      |     ^^^^^^

Listing 7-5: Error when trying to extract the `server` submodule into src/server.rs

The error says we `cannot declare a new module at this location` and is pointing to the `mod server;` line in src/network.rs. So src/network.rs is different than src/lib.rs somehow: keep reading to understand why.

The note in the middle of Listing 7-5 is actually very helpful because it points out something we haven’t yet talked about doing:

    note: maybe move this module `network` to its own directory via
    `network/mod.rs`

Instead of continuing to follow the same file-naming pattern we used previously, we can do what the note suggests:

1. Make a new directory named network, the parent module’s name.
2. Move the src/network.rs file into the new network directory and rename it src/network/mod.rs.
3. Move the submodule file src/server.rs into the network directory.

Here are commands to carry out these steps:

    $ mkdir src/network
    $ mv src/network.rs src/network/mod.rs
    $ mv src/server.rs src/network

Now when we try to run `cargo build`, compilation will work (we’ll still have warnings though). Our module layout still looks exactly the same as it did when we had all the code in src/lib.rs in Listing 7-3:

    communicator
     ├── client
     └── network
         └── server

The corresponding file layout now looks like this:

    └── src
        ├── client.rs
        ├── lib.rs
        └── network
            ├── mod.rs
            └── server.rs

So when we wanted to extract the `network::server` module, why did we have to also change the src/network.rs file to the src/network/mod.rs file and put the code for `network::server` in the network directory in src/network/server.rs? Why couldn’t we just extract the `network::server` module into src/server.rs? The reason is that Rust wouldn’t be able to recognize that `server` was supposed to be a submodule of `network` if the server.rs file was in the src directory. To clarify Rust’s behavior here, let’s consider a different example with the following module hierarchy, where all the definitions are in src/lib.rs:

    communicator
     ├── client
     └── network
         └── client

In this example, we have three modules again: `client`, `network`, and `network::client`. Following the same steps we did earlier for extracting modules into files, we would create src/client.rs for the `client` module. For the `network` module, we would create src/network.rs. But we wouldn’t be able to extract the `network::client` module into a src/client.rs file because that already exists for the top-level `client` module! If we could put the code for both the `client` and `network::client` modules in the src/client.rs file, Rust wouldn’t have any way to know whether the code was for `client` or for `network::client`.

Therefore, in order to extract a file for the `network::client` submodule of the `network` module, we needed to create a directory for the `network` module instead of a src/network.rs file. The code that is in the `network` module then goes into the src/network/mod.rs file, and the submodule `network::client` can have its own src/network/client.rs file. Now the top-level src/client.rs is unambiguously the code that belongs to the `client` module.

### Rules of Module Filesystems

Let’s summarize the rules of modules with regard to files:

- If a module named `foo` has no submodules, you should put the declarations for `foo` in a file named foo.rs.
- If a module named `foo` does have submodules, you should put the declarations for `foo` in a file named foo/mod.rs.

These rules apply recursively, so if a module named `foo` has a submodule named `bar` and `bar` does not have submodules, you should have the following files in your src directory:

    └── foo
        ├── bar.rs (contains the declarations in `foo::bar`)
        └── mod.rs (contains the declarations in `foo`, including `mod bar`)

The modules should be declared in their parent module’s file using the `mod` keyword.

Next, we’ll talk about the `pub` keyword and get rid of those warnings!