When the 2018 path changes came about, the lang team considered and dismissed the option of removing the use of mod
entirely (from standard usage) and relying on filesystem path / glob inclusion instead. The team decided that this was not the direction ideal for Rust, in large part because as a low-level language, using #[cfg]
to conditionally include some module subtree is considered to be an important use case for conditional compilation. As opposed to something like go's conventional solution of IIRC *.target.go
only getting included for the named target.
It's quite interesting to me to note that major scripting languages actually work in the same manner — for both Python and JavaScript, a file is never parsed unless you import
it. The lack of separation between mod
and use
along with less strict AOT properties and the requirement that all methods be defined in the file that defines a type make this feel a lot different than in Rust (namely, there's no impact to importing a file or not other than names that are imported and heavily discouraged top-level side effects) but it's still how the language works. And since import
is itself a dynamic instruction, whether a file is parsed can depend on arbitrary runtime conditions, although good style is generally to not do that.
If I were designing a new language, I would currently choose to use glob inclusion, with all files in a folder contributing to the same namespace. But for Rust, even with a file being mounted at multiple paths being a nightmare for IDEs, "mount" (mod
) and "import" (use
) being separate concepts is fine, good, and just something that developers need to get used to. use mod
would make the confusion worse IMO and exacerbate the learner stumbling point of trying to use mod
to access a sibling module when they want use
.
While splitting like this is sometimes justified, it generally comes from developers bringing over habits from OOP languages that are non-idiomatic in Rust. You absolutely do not need to have a separate file for each type. Large files are fine (to a point), and unless there's a massive amount of code, to a first order of approximation you should have files only for the externally exposed module tree.
Any time you write a glob import, that's a code smell that maybe you aren't doing the idiomatic thing. Sometimes it is appropriate, but this is generally for public prelude modules that are designed to be glob imported from multiple places. If you're glob importing a private mod
, the immediate question is whether it has any reason to be a separate module.
It could even be you want include!
instead.
This is generally the purpose that my lib.rs
and mod.rs
end up taking — they exist to contain documentation and mount the module tree with mod
and pub use
, with actual code in the other name.rs
files. That a module defines both (pub
) items and submodules isn't unheard of, but it's certainly an exceptional case and not the norm.