Search code examples
rustnamespaces

Organise project structure


I'm working on extending a Rust project. Coming from the C++ world, I'm looking for a namespace-like concept.

The file structure of the Rust project looks like that:

src
|
|--lib.rs
|
|--examples/example.rs
|
|--level1
|     |---verify.rs // pub fn verify(...)
|     |---keygen.rs // pub fn gen_key(...)
|     |---sign.rs   // pub fn sign(...)
|     |---mod.rs    // pub mod verify; pub mod keygen; pub mod sign; 
|
|--level2
|     |---verify.rs // pub fn verify(...)
|     |---keygen.rs // pub fn gen_key(...)
|     |---sign.rs   // pub fn sign(...)
|     |---mod.rs    // pub mod verify; pub mod keygen; pub mod sign;

I think that, acc. to Rust terminology, I have one crate and several modules with the same name (e.g. two modules called sign, derived from the filenames sign.rs).

I can access e.g. the sign() function of level1 from the implementation of level2 / sign.rs / sign() in a concise but unambiguous way:

level2/sign.rs:

use crate::level1;

pub fn sign() {
  level1::sign();
}

Afaik, the whole API for any user (as well as for example1.rs) is provided via lib.rs and each folder's mod.rs. Now I can use e.g.

examples/example1.rs:

use crate_name::*;
fn main() {
  crate_name::level1::sign::sign();
  crate_name::level2::sign::sign();
}

My questions are:

  1. Is there a way to achieve the following (leave out the filename/module name), and if yes: how?
[...]
fn main() {
  crate_name::level1::sign();
  crate_name::level2::sign();
}
  1. The files like level1/sign.rs are called modules (sign). What are the folders like level1 called? They are neither crates or modules -- is there a terminology?

  2. I see that files like level1/sign.rs are considered as modules, because the mod.rs contains a statement like pub mod sign without any declaration mod sign { ... }. So I assume there's a convention to, as default, have a module called like the source file. And other modules in the same file can be introduced via mod tests { ... }. Do I understand that correctly?


Solution

  • Is there a way to achieve the following (leave out the filename/module name)?

    If you have created modules that are not meaningful subdivisions in your crate's public API, you can and should hide them via reexports. Your level1/mod.rs presumably currently contains:

    pub mod verify;
    pub mod keygen;
    pub mod sign;
    

    It should instead contain the following. (Note that the modules themselves are no longer public.)

    mod verify;
    pub use verify::verify;
    mod keygen;
    pub use keygen::keygen;
    mod sign;
    pub use sign::sign;
    

    Or you may use glob imports to reexport everything public from every module:

    mod verify;
    pub use verify::*;
    mod keygen;
    pub use keygen::*;
    mod sign;
    pub use sign::*;
    

    When designing your public modules, I strongly recommend you run cargo doc --open and look at the produced documentation for your library, and think about whether the organization (and the documentation text accompanying it) will be clear to your users.

    The files like level1/sign.rs are called modules (sign). What are the folders like level1 called? They are neither crates or modules -- is there a terminology?

    It's just the folder where the code for child modules of the level1 module are looked for by the compiler. It is not itself any kind of element of a Rust program.

    Note also that src/level1/mod.rs is the source file for the module named level1. (You can also place it at src/level1.rs instead; this style is newer, and weakly recommended over the older style, but taste varies and there's no harm in using mod.rs.)

    I see that files like level1/sign.rs are considered as modules, because the mod.rs contains a statement like pub mod sign without any declaration mod sign { ... }. So I assume there's a convention to, as default, have a module called like the source file. And other modules in the same file can be introduced via mod tests { ... }. Do I understand that correctly?

    I would say, rather, that src/level1/sign.rs is not a module, it's a source code file; modules are items that, like other items such as functions and types, are declared in source code (mod sign; or mod sign {...}). The file layout on disk is just where the compiler automatically looks for code files based on your module declarations, and has no more significance to the program than (usually) having a distinct path for each module you declare.

    Think about your mods as being the fundamental organization of your crate, and source file layout as a convention for how to store the code for the mods in a predictable location. (It's possible to direct the compiler to look for a module's source code in a different location — but don't do that, because it confuses readers, and situations where it looks like that is a good option are usually better handled other ways.)