Module structure problem - single parent class, multiple child classes

I am the Rust beginner in the process of porting the C++ code to rust. These are the details of the original design:

The C++ library features a class which is parent class of several other classes. All these other classes reside at the same hierarchy level (siblings basically).
Say:
class A : public M;
class B : public M;
class C : public M;
...
class N : public M;

By using the Rust's traits and composition, I've successfully ported the 'class A: public M' part. At this same moment, I notice that the Rust modularization rules stipulate certain directory structure when classes depend on each other, and according to these rules suggested by the compiler my Rust library src folder should have the following structure:
src/
src/A
src/A/M

But, when the class B is implemented, under the same set of rules I should have
src/
src/B
src/B/M
which seemingly in the long run leads into having N subdirectories for the same parent class.

What would be the way to circumvent these kinds of situations ?

Basically, is it possible to somehow nudge the compiler to live with the flat directory structure of classes source files ?

There is a default 1:1 mapping between files in the filesystem and module structure, but Rust doesn't care about relationship between traits and modules. Module structure is arbitrary, and you can choose any structure at your convenience. You can put all your traits in the same module, you can put them all in sibling modules, you can nest them arbitrarily deep — it doesn't matter.

You only need to make traits public to be able to use them outside of the module they are defined in. Change trait M to pub trait M, and then in other files add use crate::name_of_the_module_with_m::M.

BTW: Rust doesn't have inheritance. trait A: B doesn't inherit B. It means that whoever implements A also needs to implement B, but A and B remain separate. This is a subtle difference that shows up in some cases, e.g. Box<dyn A> can't be used in place of Box<dyn B>.

6 Likes

Thanks for the reply.

The problem I am facing now is that the cargo build kind of nudges me to maintain the folder structure that reflects the modules dependencies. When I establish the suggested directory structure, the code compiles and runs fine. But, my every attempt to keep all the modules in the same folder fails (file not found for module 'xyz').

Is there a way to keep all the modules in the same directory, and if so, what are the key ingredients of the recipe ?

Can you be more specific about the problem you're having? As @kornel mentioned, module structure is arbitrary. Flat or nested structures should work about equally well. I don't understand why you're saying the compiler is "nudging" you to put dependencies in subdirectories; it has a lot of suggestions but I can't remember it ever suggesting that I split up modules differently.

I suspect you're not using pub or have problems with specifying correct paths in use, which makes you use nested modules instead.

mod foo {
    struct Foo;
    mod bar {
        // can use Foo here
    }
}

but you don't need the nesting if you do:

mod foo {
    pub struct Foo;
}

mod bar {
    use crate::foo::Foo;
    // can use Foo here too
}
1 Like

Another thing that trips a lot of people up when learning the module system - the following three examples are treated exactly the same by the compiler:

Single file:

// lib.rs
mod foo {
    pub struct Foo;
}

mod bar {
    use crate::foo::Foo;
    // can use Foo here too
}

Multiple files (extremely unidiomatic, don't do this):

// lib.rs
mod foo {
    include!("./foo.rs");
}

mod bar {
    include!("./bar.rs");
}
// foo.rs
pub struct Foo;
// bar.rs
use crate::foo::Foo;

Multiple files (idiomatic):

// lib.rs
mod foo;
mod bar;
// foo.rs
pub struct Foo;
// bar.rs
use crate::foo::Foo;

In other words, mod always creates a new module (whether the contents is specified inline or in a seperate file).

1 Like

The compiler error message was suggesting clearly to create a subfolder named as the dependent module in which another module would be placed.

Have eventually found the solution with a lot of help from workplace colleagues. The essence of the solution is to refer to the module as crate::::blablabla

Can you give an example? If this error is misleading, it should probably be fixed.

I bet @traveller was mistakenly using mod foo; instead of use foo;.

error[E0583]: file not found for module `foo`
 --> src/main.rs:1:5
  |
1 | mod foo;
  |     ^^^
  |
  = help: name the file either foo.rs or foo/mod.rs inside the directory "src"

Perhaps another help: line would be in order?

help: to import the sibling module named `foo`, try `use crate::foo;`
2 Likes

Given how often this comes up, I would think it needs to be even stronger:

help: This code creates a new module named `foo`.
      If you meant to use the existing module `crate::foo`, import it instead with `use crate::foo`.
4 Likes
5 Likes

The 'crate::foo' was exactly the remedy for the problem. Thanks for the help !