mod statement based mounting system for modules is a desirable feature for many people writing lower-level code, because it allows developers to write
#[cfg] mod and similar, conditionally including (and excluding) entire file/module trees from inclusion in the compilation.
Similarly, the capability to define a type in a different namespace from where it's exported to the outside world is an important option in Rust, because of how privacy is used to encapsulate
unsafety. Without the ability to introduce an extra API-invisible privacy barrier, my structure's private state is going to be accessible by more code, and I have to trust significantly more code to maintain my invariants.
And even if there's no
unsafe anywhere in the crate, safe invariants benefit just the same, and being able to split up file hierarchy based on implementation rather than exported interface, having actual private namespaces, is extremely useful. Yes, it can sometimes be a bit nonobvious where some type/function is defined, but 99% of the time you can go from the export position (which is filesystem-tied) and trace reëxports back to the definition site; glob reëxports are generally discouraged outside of hyper-specific cases (e.g. bevy's prelude-of-preludes).
Java's organizational system is entirely conventional, by the way. There's no actual requirement that
.java source files match their package location on the filesystem; the
package declaration at the start of the file is all that matters. (Most tooling assumes this layout, but none of the core tooling requires it, IIRC.
.class files and the classpath lookup is fs dependent, and
.jar is just a fancy
There's also the fact that for Java, the unit of compilation is each individual
.java file, and the build system is in charge of compiling each
.java and packing together all of the individual
.class files created by that compilation. In Rust, however, the unit of compilation is the entire crate; the compiler
rustc gets the path to
lib.rs and discovers the rest of the crate via the
mod statement mounting points. The
cargo build system does very little for the local crate; its real work is in managing dependency crates.
The module/path system already underwent a significant migration from 2015 era Rust to edition 2018 and beyond.
mod mounting statements not inside an index module
main.rs; the extern crate namespace of
::lib being a distinct namespace from
crate:: being names at the root of the current crate.
Rust is kind of unique in that it has two ways of interacting with the module system:
mod to mount modules, and
use to bring names into scope from mounted modules. In scripting languages with similar relative-path-based
import, mounting and using a file are the same operation, and importing the same file more than once typically doesn't redefine its symbols. Rust on the other hand is perfectly happy to allow you to mount the same file more than once with
mod. (If we don't already have default diagnostics for when people do this, we absolutely should.) Private module paths are also somewhat unique of a concept to Rust, that I haven't seen elsewhere.
But Rust is also surprisingly good at noticing when you've made a mistake (e.g.
used a path through a private module,
used an unmounted module) and making suggestions as to what you wanted to write. Any specific cases where the compiler could reasonably do a better job of inferring intent absolutely should be tracked as issues; making the compiler more helpful on invalid code is one of the best superpowers of the Rust culture.
Would I make different decisions were I designing a new language from scratch today? Probably! But Rust's module system is established enough that it's not going to undergo any drastic adjustments. Especially not via some vague "do it better" ultimatum; at a minimum you would need to provide a draft of what you would want the new system to look like, what benefits it would bring, and what the migration path looks like. Even with that, though, the chance of making large changes (e.g. removing the need for
mod statements) is quite unlikely.
To control access to published modules there are many possibilities [...] it's just ridiculous the [...] restrictiveness of the module system...
This seems contradictory. Either there are a lot of (potentially redundant style-only) options, or the system is restrictive. You can't really claim that the module system gives you both too many and too little choice in how to express your API and implementation structure.
For what it's worth, the Rust system is the most expressive module/namespace system I've used. No other language has both proper module/namespacing-by-default support and allows you to export an API item independent from where it's defined. I don't feel any sort of restriction in what the language's namespace system allows me to express.