10

I read the mod document and found that I must add mod.rs in each folder, for example I have to define mod like this in my project structure:

src/
  models/
    login.rs
    mod.rs
  routes/
    login_route.rs
    mod.rs
  services/
    login_service.rs
    mod.rs
  main.rs

and use it like this in main.rs:

mod models;
mod routes;
mod services;

Why do it like that? Why design like that? If the project increase, the project has many mod.rs file just for expose mod? It is a good practice? I did not understand. What is the advantage about do it like this way? If we just do it like this way:

crate::models::login;

it is so clear and easy to understand.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Dolphin
  • 29,069
  • 61
  • 260
  • 539

4 Answers4

19

Some of this is explained in What is the purpose of a mod.rs file?

Essentially Rust does not make any assumptions about the file structure and won't consider other .rs files without the developer declaring them. Instead it is intended that they use the module system to build up an organizational structure within their code.

While this differs from some other languages, this gives more control to the developer. Since import paths are decoupled the file structure, your modules, structs, functions, etc. can be re-organized and re-exported as necessary. This also allows you to declare exactly what is compiled, which can aid in conditional compilation.

So when does Rust use the file structure? When you declare a module without a body:

mod models;

It will look for a file to use for that module.

  • from a normal file, like utils.rs for example, it will look for a nested file/directory:

    ./utils/models.rs
    ./utils/models/mod.rs
    
  • from mod.rs or top-level lib.rs or main.rs (they are special in this regard), it will look for a sibling file/directory:

    ./models.rs
    ./models/mod.rs
    

The use of mod.rs conceptually allows you to use a directory as if it were a file (it is similar to index.js if you're familiar with Javascript).


Is there a way to avoid all these mod.rs files?

There are two decent alternatives:

  1. Use models.rs instead of models/mod.rs

    Simply move your mod.rs files up one level and rename them to match their directory. You do not have to modify contents of main.rs or any file. Your structure could look like this:

    src/
      models/
        login.rs
      routes/
        login_route.rs
      services/
        login_service.rs
      main.rs
      models.rs
      routes.rs
      services.rs
    

    This is slowly becoming the preferred method[citation needed] since it gives the files more descriptive names.

  2. Simply declare your module structure within main.rs:

    This is a bit unconventional, but nested module declarations will find nested files. You don't need mod.rs anywhere:

    src/
      models/
        login.rs
      routes/
        login_route.rs
      services/
        login_service.rs
      main.rs
    

    If you declare your modules in main.rs like so:

    mod models {
        mod login; // this will use ./models/login.rs
    }
    mod routes {
        mod login_route; // this will use ./routes/login_route.rs
    }
    mod services {
        mod login_service; // this will use ./services/login_service.rs
    }
    

    This isn't particularly recommended. It may work fine in small projects, but will become unwieldy as your codebase gets larger. You'll probably want to reach for mod.rs or the method shown above as a way keep your code composed.


Lastly, these transitive modules are not just for declaring other files. They're a convenient place to:

  • put shared code used within its sub-modules
  • include documentation for how its contained structs, traits, etc. are designed and/or meant to be used
  • re-export deeply nested items so they are more accessible to the rest of your code

Overall, the module system is just another level of abstraction to keep your code well-encapsulated.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • 1
    I've seen option 1 used in a few projects, and I think it's a mistake. I don't see how models.rs is any more descriptive than models/mod.rs. If anything, it's less descriptive, and it's confusing because its unclear why the file exists until you locate the folder. It makes more sense for the folder to encapsulate its own module setup rather than cluttering the top level with a bunch of scattered duplicates that are confusingly amputated. – Drew Nutter Aug 11 '22 at 23:13
  • > Rust does not make any assumptions about the file structure ...while making copious assumptions, as elaborated on by the rest of your answer :( – allidoiswin Jul 11 '23 at 22:45
  • @allidoiswin The full sentence qualifies that quote: just because a `.rs` is in your `src/` directory, doesn't mean its compiled. That's all that means. You have to opt in to what files are compiled and obviously you have to reference files you *do* want by name (`mod name;`) and those are relative to the current file. Not sure if you are quantifying that as an "assumption" but to each their own. – kmdreko Jul 11 '23 at 23:19
  • Yes, the assumption is that either `./name.rs` exists or `./name/mod.rs` exists. The question then is why it doesn't go a step further: if neither exist but `./name` (or `./name/`) exists, then treat `mod name;` as the same as if we had an empty `./name/mod.rs`. – allidoiswin Jul 13 '23 at 23:21
  • @allidoiswin Sounds like that'd be making *more* assumptions not less. But either way, StackOverflow isn't really the place to complain about language design. – kmdreko Jul 13 '23 at 23:56
5

Since Rust 2018 edition, the mod.rs file is optional:

In Rust 2015, if you have a sub-module:

// This `mod` declaration looks for the `foo` module in
// `foo.rs` or `foo/mod.rs`.
mod foo;

It can live in foo.rs or foo/mod.rs. If it has sub-modules of its own, it must be foo/mod.rs. So a bar sub-module of foo would live at foo/bar.rs.

In Rust 2018 the restriction that a module with sub-modules must be named mod.rs is lifted. foo.rs can just be foo.rs, and the sub-module is still foo/bar.rs. This eliminates the special name, and if you have a bunch of files open in your editor, you can clearly see their names, instead of having a bunch of tabs named mod.rs.

Other resources:

Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
1

Add a Path attribute

It is exactly like you mention in your question, but with a small annotation. This example should show how to do that:

Example

Using the same directory structure as in the OP question:

src/
  models/
    login.rs
  routes/
    login_route.rs
  services/
    login_service.rs
  main.rs

The main.rs file can declare the modules as follows:

#[path = "models/login.rs"]
mod models;

#[path = "routes/login_route.rs"]
mod routes;

#[path = "services/login_service.rs"]
mod services;

The Pros of this approach is that your mod <module_name> need not be the same as the original file or directory -- allowing for shorter names when needed.

The Con, however, is that this won't be able to refer to multiple modules in the subdirectory. (For example, if the models/ directory had more modules inside of it, it becomes a problem.)

diviquery
  • 569
  • 5
  • 19
  • 2
    Another con and the reason why I didn't mention it as a solution in my own answer: it is a very tempting tool since it seemingly allows you to structure your files however you like, but it should not be used to blindly access another file. If you have two `#[path = ...]` attributes that refer to the same file, they will be *different* modules with the same content and thus you can hit strange warnings or confusing errors from doing so. – kmdreko Mar 25 '23 at 16:35
  • That's right. For some specific usecases, this suffices though. It's one way of doing so, perhaps not the best... – diviquery Mar 25 '23 at 20:07
-1

The question seems have gone further to "Is it possible to avoid both mod.rs and <directory>.rs" or "if neither exist but ./<directory> (or ./<directory>/<file>) exists, why not then treat mod name; as the same as if we had an empty ./<directory>/mod.rs." as @allidoiswin has mentioned.

Such a file was probably unavoidable. This could be because a directory only indicates the presence of a node in the module tree and does not describe the node. However, mod in a .rs file can fully describe the information of the node(e.g. functions in the mod, the visibility of the submodule).

Example

Suppose we want to move the mod house in main.rs to a directory.

.
└── main.rs
// main.rs
mod house {
    mod guest {}
    pub mod host { 
        pub fn clean() {
            super::clean_house();
        }
    }

    fn clean_house() {
        println!("Cleaned!");
    }
}

So we make the directory this way and want to avoid house.rs.

.
├── house
│   ├── guest.rs
│   └── host.rs
└── main.rs
// main.rs
mod house;
fn main() {
    house::host::clean();
}

// host.rs
pub fn clean() {
    super::clean_house();
}

But we found no where to write the the clean_house() function and give visibility to mod host, because house is a directory rather than a rust code file.

And the solution is to add a house.rs file, which provides the extra information of the directory house.

.
├── house
│   ├── guest.rs
│   └── host.rs
├── house.rs
└── main.rs
// house.rs
mod guest;
pub mod host;

fn clean_house() {
    println!("Cleaned!");
}

If we consider that house and house.rs are co-active, and that the house directory is where submodules of house.rs are stored, then there may be some consistency.

  • 1
    I don't think this is the place for this. Yes, this question was asked in a comment, but it is not the question in this Stack Overflow question. If you believe this is important information, you can open a question and self-answer it, but beware that your question will probably get closed as opinion-based (and perhaps also downvoted). – Chayim Friedman Aug 30 '23 at 18:14