My concern is precisely the opposite: each system in itself makes sense but what happens when a user needs to combine both systems in the same crate? E.g. the user prefers implicit modules except for that one module where they need to specify a custom #[path] attribute?
I’d prefer a different trade-off:
###Physical layout
rustc foo.rs
Currently, this compiles foo.rs and all transitive files linked via mod bar; declarations.
This should become a warning to inform the user they are using the legacy system and advise them how to upgrade. Once upgraded, the same algorithm remains the default but now it’ll resolve to just foo.rs.
Rustc would then learn to accept a set of sources and that could also be specified in Cargo.toml per artifact and passed along to rustc. This can be made expressive enough to allow dirs and globs specified in addition to plain files and cargo would help manage that as it already manages other build configuration aspect such as optimization level.
IDEs such as intellij can easily keep the Cargo.toml updated for the user and provide the magical it just works experience for the users.
Logical layout
Now that rustc already obtained an explicit set of sources we can define implicit modules without stepping on someone’s temporary “junk” files.
\\ with_implicit.rs
fn foo() {}
mod abc::def {
fn goo() {}
}
This defines everything “outside” any module to be “internal” implementation details of the current file. (should be equivalent to an anonymous namespace in C++, and we should allow to parallel this with an anonymous inline module as well. iow:
mod foo {
mod bar {
mod {
fn f() {}
}
f(); // ok, parent module has access
}
f(); // not ok, outside of parent module
}
Back to our previous example, two things of note:
- It is very important to allow nested module declarations in order to prevent rightwards-drift. e.g.
mod abc::def {}
- As is today, the path is relative to where the module is “mounted”. Without any
mod name; statements (which we deprecated above) this is reduced to be relative to the crate root. Thus this subsumes all the separate “advanced” usages into a single system.
Accessibility
- We already define internal modules without any additional syntax.
-
pub can now become truly public (world-accessible) and the path is the original module path, as long as there are no anonymous modules in the way.
-
crate can be added to mean crate-wide visible and the default “nothing” remains as private.
Note, we don’t need any accessibility modifiers on the modules themselves anymore and we can just ignore them or issue deprecation warnings. This accomplishes more with less syntax variation and a simplified and unified model.