Rust Project Folder Structure (Lib/Tests/etc.)

Coming from C++ I am trying to figure out what the equivalently best way is to structure a Rust project.

For demonstration purposes, let us assume we have this github.com/thomastt5/dummy_lib:

Now, in C++, to use class Date from dummy_lib_cpp/datetime/date.hpp

namespace dummy_lib
{
    struct Date { };
}

All I need to do is #include this header file, and then I am done.

In Rust, my current solution is as follows:

  1. Create src\datetime\date.rs containing
    pub struct Date;
  1. Create src\datetime\mod.rs containing
    mod date;
    pub use self::date::Date;
  1. Create src\lib.rs containing
    mod datetime;
    pub use datetime::Date;

My questions are:

  • Looking at the project trees above, a lot more files are required for Rust, namely 1 mod.rs in every (sub)-folder. Is this indeed the preferred way of structuring a project, or are there better ways? I ask, because cluttering my project tree with mod.rs files & having multiple mod.rs files open in my IDE, seems very confusing. The C++ project tree is much cleaner.

  • As per steps 2 & 3 above, it seems we need to use a lot of mod & use statements to cascade each struct / fn / etc. down all the way into the lib.rs file. Just to have everything in my library in a common "namespace". In C++, on the other hand, I just define my desired common namespace in each header file and then #include the file. So in C++ I am not required to propagate each struct down the folder structure to my library root. Is it possible to reduce the use of mod & use boilerplate code in Rust?

Ultimately, I want to structure my project files logically into different sub-folders, so that it helps people understand the flow of the library. But without having to create modules and visibility layers for every sub-folder in my project, as that just seems to add extra work & confusion. Of course, I am happy to manage all external visibility questions at my root lib.rs.

Apologies if I am missing anything obvious. I am still new to Rust. :slight_smile:

In Rust modules can exist without having a file on disk. If you write mod foo; it has to have foo/mod.rs, but if you write mod foo { same content that was in the file } you still get the same module in the same place, but you don't need a file for it.

You translated directories into modules. In Rust the directory structure follows code structure, not the other way around. Instead of scheduling/schedule and scheduling/annual you'd probably have schedule module with actual code, and schedule::annual if needed.

Other than that the structure you have seems ok. Alternative way to test is to have tests in the same file as the source file they test (mix both together). Rust projects also tend to have a flat structure in tests, because the 1:1 correspondence with sources isn't needed, because when it's needed, the tests go to those source files.

I am not actually trying to create any modules, just directories to logically separate the objects in my library. How would one achieve this?

Rust doesn't organize code in directories, it organizes it in modules.

If you insist, there's possibility of overriding paths like this:

#[path = "directory/foo.rs"]
mod foo;

but this is rarely used, and rather odd. Don't think about putting files in folders. Think about organizing your code logically how functions or namespaces relate to each other.

1 Like

Doesn't it help to put files into different folders? If my library has say 30 structs, I'd want neither all 30 structs + implementations in 1 single file, nor would I want 30 lose files in a single folder. Rather, I'd want a certain number of sub-folders for each struct category. So that each sub-folder can contain a smaller list of files, which is easier to understand for the user & easier to maintain. Does that make sense?

I'm not saying to put things in one file. I'm saying to split files by logical functionality, not arbitrary numbers of files.

In Rust module structure affects privacy of fields (child modules can access private fields of structs defined in their parents). It's not for "oh, that file is too big, I'll put stuff elsewhere". It's for "FooBar feature has Foo part and Bar part, each in their namespace".

In your case I'd do something like:

  • date
  • time
  • delta (for both date and time, because it's neither child nor parent of these)
  • schedule
    • annual
    • monthly

or if delta has to be split, but has some common code, then:

  • date
  • time
  • delta
    • date
    • time

or if deltas don't have any commonality, they could be tucked into their parent modules:

  • date
    • delta
  • time
    • delta

If you like your current structure with directories that don't represent any code by themselves, then make dummy modules for them. But if you're wondering how it would be properly structured in Rust, then this extra directory level would not exist.

There may one not odd usage: test derive macro codes. Since a proc-macro lib is loaded by compiler, at compile time, it's hard to test the codes, by using this, logic codes can be "extract" into a normal crate (as long as you use proc-macro2 to generate token streams), then testing, free the proc-macro crate from depending on a real crate, which could be problems when publishing.

Using this tech rather than using a symbolic link is that it is platform compatible.

Any submodule/mod.rs file can be replaced with a submodule.rs, which you may find cleaner.

Can I also put the submodule.rs file into submodule/submodule.rs, so that everything related to this module is contained in the same folder?

You'd have to override that with #[path]. The default paths are xxx.rs or xxx/mod.rs only.