Agreed.
However, I don't believe the directory-based proposal will make it any easier- and I worry that it will make it harder, especially given the existence of the current system alongside it. _
also seems a very magical, non-obvious mechanism that's hard to search for, as opposed to pub(crate)
which is easily searchable and has analogs in other languages (e.g. internal
in C#).
I'm also not convinced that the reform even needs to be primarily a change to how the module system works. Previously I suggested some new compiler messages that might make the current system easier, and others have proposed much smaller tweaks to the current system that address the concerns in the post. We could also take a look at other languages that aren't known for confusing module systems.
For example, Python:
- A file is a module is a file, much like Rust, but without supporting
mod name {}
.- A directory must have an
__init__.py
file in order to be a module and contain submodules, much like Rust'smod.rs
. Other files in the directory are submodules, just like Rust. __init__.py
has to include an__all__
variable forfrom something import *
to bring in any submodules. Submodules not listed can still be brought in explicitly, however.
- A directory must have an
- It has two forms for pulling in a module (
import something
andfrom something import ...
).import something
is identical in namespacing behavior tomod something
.from something import *
is similar to the proposedinline mod
; equivalent to the currentmod something; use something::*;
without bringingsomething
into scope.
- Both absolute and relative paths are used
- in
import
s in they are relative to a list of search paths, much like Rust's list ofextern crate
s and including the main entry point. - in regular code they are relative to the current module
- in
Translating this to Rust and the post's issues, we should be able to gain a lot of familiar behavior with a much smaller (and backwards-compatible!) proposal:
- For directories containing
mod.rs
(orlib.rs
ormain.rs
), automatically include neighboring files and neighboringdir/mod.rs
es as private submodules. This preserves single-file modules and still cuts down on declarations. - Let
mod.rs
expand submodules' visibility and assign attributes with explicitpub mod
orpub(crate) mod
declarations, somewhat like Python's__all__
. Error messages for this situation would look like "the modulefoo::something
is private; declare itpub
infoo/mod.rs
.
For the beginner case, this brings us back from two forms (mod
and use
) to just one (use
). Create another file, put some code in it, and either reference it directly with other_file::function
or use
it with use self::other_file::function
or use self::other_file::*
.
We lose protection from accidental extra files, which Python has because it's dynamic and they're never import
ed, but at least we don't get accidental other directories. We also lose the ability to comment out a module declaration, but we keep something close by renaming its file extension instead (including of mod.rs
).
I believe this addresses the learnability and repetition problems without changing things too drastically. extern crate
could also help, and inline mod
(or with this proposal maybe just use something::*
) should help with the facade issue. How does this sound, to proponents of the current system and to new users?
edit: Just read the recent extern crate
inference RFC. This seems to line up quite nicely with it, including the concept of adding back in extern crate
/mod
declarations to add visibility, aliasing, and attributes to them.