Module nesting and visibility


#1

Hi all,
I’m trying to use the pub mod functionality to change the signature of public structs. I, am, however, stuck on how to make public modules which are nested in remapped modules (with pub use).

For example if I have this tree structure:

│   main.rs
│
└───locomotion
    │   mod.rs
    │
    ├───bike
    │       enums.rs
    │       mod.rs
    │
    └───car
            enums.rs
            mod.rs

Notice I have two enums modules, one in bike and one in car. I would like them to be exported as

  • locomotion::{Bike, Car}
  • locomotion::car::enums::*
  • locomotion::bike::enums::*

But I haven’t been able to do it :frowning:


locomotion/mod.rs

mod bike;
mod car;

pub use self::bike::Bike;
pub use self::car::Car;

I can access Bike and Car in main.rs with:

mod locomotion;
use locomotion::{Car, Bike};

The problem here is that I cannot access locomotion::car::enums::* because they are private:

I’ve tried two things:

make car module public changing locomotion/mod.rs

locomotion/mod.rs

mod bike;
pub mod car;

pub use self::bike::Bike;
pub use self::car::Car;

This works - I can use locomotion::car::enums::* without errors. However I can also do this:

main.rs

use locomotion::{Car, Bike};
use locomotion::car::Car as Automobile;

Basically I can import the same module twice with different names (super-confusing IMHO).

Reexport the locomotion::car::enums::Color enum both in locomotion/car/mod.rs and in locomotion/mod.rs:

locomotion/car/mod.rs

mod enums;
pub use self::enums::Color;

#[derive(Debug, PartialEq)]
pub struct Car {
    pub c : Color
}

locomotion/mod.rs

mod bike;
mod car;

pub use self::bike::Bike;
pub use self::car::Car;
pub use self::car::Color;

This also works as I can import the Color enum in main.rs simply like this:

mod locomotion;
use locomotion::{Car, Bike, Color};

I don’t like this approach as it bring the Color enum in the locomotion namespace. I would like it to stay under the locomotion::car namespace instead. This way I would clearly state that Color applies to Car only and not to Bike.

How can I do that?

Thank you for reading this looong question :slightly_smiling: !


#2

You create a façade like this:

mod locomotion {
    pub use self::car_impl::Car;
    pub use self::bike_impl::Bike;

    pub mod car {
        pub use super::car_impl::enums;
    }

    pub mod bike {
        pub use super::bike_impl::enums;
    }

    mod car_impl {
        pub mod enums {}
        pub struct Car;
    }

    mod bike_impl {
        pub mod enums {}
        pub struct Bike;
    }
}

There is a simpler way to do this:

use std as foo;
use std as bar;

The problem here is not with different names for items, but with different access paths to the item.


#3

Brilliant @matklad, thank you! :thumbsup:

It’s a bit convoluted though… I had to shuffle around and rename a lot of files. I think I tend too much to follow the old java approach “one class, one file” as it doesn’t translate well with rust. I came up with lots of useless modules. I’ll have to rethink about it.

Thank you again,
Francesco


#4

It’s a bit convoluted though.

Yes. I tend to use hierarchical implementation/flat façade style. It is also ok to have several structs / enums in one mod. For your use case, I would do something like

mod locomotion {
    mod car;
    mod bike;

    pub use car::{Car, CarColor};
    pub use bike::Bike;

    mod car {
        struct Car;
        struct CarColor;
    }

    mod bike {
        struct Bike;
    }
}

If the size of car module gets too large, I will introduce car::car_colors and add pub use car_color::CarColor to car, so as not to change anything outside the car tree.

If you don’t have pub mods, you can change module structure at any moment.