Idiomatic module hierarchy organization?

Hi peoples, :slight_smile:

My code is slowly growing and I start to separate things. Here is my package structure:

engine/
  src/
    ecs/
      blah.rs
      foo.rs
    ecs.rs
    util.rs
    lib.rs
    main.rs
  tests/
    ecs_test.rs

As you can see, I have a ecs module definition:

// ecs.rs
mod blah;
use blah::my_func;

With that, I started by putting mod ecs; in main.rs:

// main.rs
mod ecs;

fn main() {
    ecs::my_func()
}

But in order to build my tests, I realized I can't call use crate::ecs::my_func; inside them, but use the absolute crate path use engine::ecs::my_func;.

// tests/ecs_test.rs
use engine::ecs::my_func;

This makes senses as tests are separated binaries so I suspect its legit.

To do so, I needed to have lib.rs with pub mod ecs;:

// lib.rs
pub mod ecs;

Doing so, I realize I'm in a kinda mixed crate definition cause by having both main.rs and lib.rs. :confused:

For example, to call foo.rs from blah.rs I must call super:::

// blah.rs
// use crate::foo.rs // doesn't work
use super::foo.rs

But here is my problem now: I want to use functions from util.rs inside ecs/blah.rs without relying on the absolute path: I don't want my code inside ecs/ to have use engine::.

// ecs/blah.rs
// use engine::util.rs; // I don't want that.
use ?

Regarding this, I think I am wrong in the way I organize my work. I think my problem is related to main.rs/lib.rs mix in crate definition, but depicts looking in various places, I can't find nice solution.

The book explains how module system works, but not how you should organize your code base.

So here I am, asking to you peoples: How would you organize this repo? Of course, the number of submodules would grow in time:

engine/
  src/
    ecs/
      blah.rs
      foo.rs // calling mod1 and util.rs
    ecs.rs
    mod1/
      blah.rs // calling util.rs
      foo.rs
    mod1.rs
    util.rs
    lib.rs
    main.rs
  tests/
    ecs_test.rs
    mod1_test.rs

Thanks in advance.

Every file should have exactly one mod declaration, and all other uses should be use declarations. It belongs in lib, and to access lib.rs from main.rs, you should use it as if it was a separate crate.

// main.rs
use whatever_you_named_your_package_in_cargo_toml::ecs::*;

Tests are also separate crates, and should use the same path. Tests cannot access anything defined in main.rs.

1 Like

Thanks @alice!

I just tried and it worked perfectly:

  • All mod foo in lib.rs.
  • Full path in main.rs and tests.
  • use crate:: everywhere.

Last question: This also mean I need to put everything in lib.rs in pub in order to test them? Is there special rules to avoid marking module public but allow tests to work?

Thanks again for your precious help! :slight_smile:

Tests in the tests/ folder can only access public items. You can use a test module inside the library to test private things.

// your file.rs
#[cfg(test)]
mod tests {
    // test private things in your_file.rs here
}
1 Like

Thanks! Both could coexists:

  • Private tests for library internals.
  • Public tests for library API.