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__.pyfile 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__.pyhas 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 somethingandfrom something import ...).import somethingis identical in namespacing behavior tomod something.from something import *is similar to the proposedinline mod; equivalent to the currentmod something; use something::*;without bringingsomethinginto scope.
- Both absolute and relative paths are used
- in
imports in they are relative to a list of search paths, much like Rust's list ofextern crates 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.rsormain.rs), automatically include neighboring files and neighboringdir/mod.rses as private submodules. This preserves single-file modules and still cuts down on declarations. - Let
mod.rsexpand submodules' visibility and assign attributes with explicitpub modorpub(crate) moddeclarations, somewhat like Python's__all__. Error messages for this situation would look like "the modulefoo::somethingis private; declare itpubinfoo/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 imported, 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.