4

I have a Rust module breakfast containing two sub modules egg and bacon. The breakfast module must know about egg and bacon, but the two children do not need to and therefore should not know about each other.

This is what my code looks like now. The breakfast gets made, but unfortunately egg and bacon can access each other.

mod breakfast {
    pub fn make_breakfast() -> String {
        format!("{} and {}", egg::EGG, bacon::BACON)
    }

    mod egg {
        pub const EGG: &'static str = "egg";
    }

    mod bacon {
        pub const BACON: &'static str = "bacon";

        // Oh no! The bacon knows about the egg!
        // I want this to be a compile error.
        use super::egg::EGG;
    }
}

Can I hide the siblings from each other somehow, perhaps by using visibility modifiers or by restructuring the modules? Or is the unneeded visibility something I should accept?

In reality, the modules are in separate files, but I put them in one here to make a clearer example.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Anders
  • 8,307
  • 9
  • 56
  • 88
  • Seems that modules in one file always see each other. For modules in separate files, you can try to restrict visibility using `pub (super)` syntax. – Zefick Aug 06 '19 at 10:09
  • @Zefick Thanks for the tip, will try it out! The modules are actually in separate files, just put them in one to make the question clearer. – Anders Aug 06 '19 at 10:13
  • 2
    To the best of my knowledge, file structure does not affect the way Rust sees your program's module structure. e.g. `mod breakfast { ... }` and `mod breakfast;` with a seperate file will always function exactly the same. – Joe Clay Aug 06 '19 at 10:15
  • 5
    @Zefick it has nothing to do with separate files actually -- modeling modules the way Anders did, and splitting them into separate files are the exact same thing. – Peter Varo Aug 06 '19 at 10:15
  • 1
    Now I see. `pub(super)` makes a module visible in a supermodule and all its submodules. – Zefick Aug 06 '19 at 10:21

1 Answers1

2

This is by design:

Rust's name resolution operates on a global hierarchy of namespaces. Each level in the hierarchy can be thought of as some item. The items are one of those mentioned above, but also include external crates. Declaring or defining a new module can be thought of as inserting a new tree into the hierarchy at the location of the definition. [...]

By default, everything in Rust is private, with two exceptions: Associated items in a pub Trait are public by default; Enum variants in a pub enum are also public by default. When an item is declared as pub, it can be thought of as being accessible to the outside world.

With the notion of an item being either public or private, Rust allows item accesses in two cases:

  • If an item is public, then it can be accessed externally from some module m if you can access all the item's parent modules from m. You can also potentially be able to name the item through re-exports. [...]
  • If an item is private, it may be accessed by the current module and its descendants.

For further information on this, please consult the relevant chapter of The Reference

Peter Varo
  • 11,726
  • 7
  • 55
  • 77
  • 2
    Interestingly, in C++ and Java, child classes have access to everything their parent class can access, so the Rust rule seems consistent with C++ and Java in this regard. I wonder if there are languages behaving differently. – Matthieu M. Aug 06 '19 at 10:59