Yeah I just think it’s a trend with rust where when a new user complains about a beginner-unfriendly feature, often they are met with an explanation of some esoteric benefit.
I just wonder how much of that is as-hoc reasoning, and it doesn’t seem like it’s a particularly good sign.
I thought this way too until I saw a bunch of people praising how easy the module system was because they felt it was very similar to Python's. I don't know Python well so I can't speak to it.
My working theory: each language does modules in a different way. People think "Oh, a module system, I know this" and then run into issues when it works differently than their language. Just a theory though, I have not been able to validate it, or to figure out an explanation that works for all people.
And yes, more generally: as Rust continues to grow, more and more people will hit more and more edge cases, and they cannot always be fixed, thanks to being backwards compatible. That's just how things are as they grow up. More and more users also brings more and more use cases. It's a feedback loop.
Oof I dunno I'd categorize the Python module system as "makes no sense and I can never get it right" - I'm sure it's consistent but it doesn't feel approachable to me.
Honestly, that idea makes a lot of sense to me, and personally, I find the module system pretty normal seeming for a modern language. I'm used to Perl and CPAN, and it's pretty similar to that, except for the option to use dir/mod.rs, which honestly seems like it's kinda nice for keeping a module contained nicely for those that want to do it that way.
Now I'm more interested in what the complaints about it are, and specifically what they're in comparison to. Either they're not used to using so many modules in a method such as this, or they're honestly expecting some better system for their use case that I'm unaware of, or a little of both, so my curiosity is piqued.
It just really depends. I should have been writing these down over the years. A few common points of confusion off the top of my head:
People expecting to put "mod foo;" at the top of foo.rs, to declare that foo.rs is a module.
People expecting to put "mod foo { }" with the contents of the file inside the {}s to declare that foo.rs is a module.
People expecting that "use" declares a module, not "mod."
People expecting every file inside a directory "foo" to be the contents of the "foo" module, regardless of filename, all concatenated together.
People expecting that mod statements never need to exist because it should be inferred from the filesystem.
General confusion about the privacy rules; that "pub" may not mean that your thing is globally public.
General confusion about crates vs modules; main.rs and lib.rs being in the same directory, but declaring different crates. Not understanding how to import stuff from lib.rs into main.rs, because it feels a bit different than other modules.
People used to also struggle a lot with "use" and paths pre-Rust 2018, but that's been mostly cleaned up at this point.
So a point of comparison would be like a Swift or a Java where module/package definitions are defined implicitly, based on the location in the file system. In contrast, Rust's explicit module declarations seem cumbersome and unnecessary, especially since 90% of the time you're just typing out a structure which is identical to what already exists in the file system. So this could easily be inferred, but it's just not.
I think the same can be said for match statements on enums: in Rust you need to match against the fully qualified type name, when in other languages (including Zig) you can infer everything up to the enum case, since it's already specified by the type you are matching against.
These kinds of things just seem weirdly inconsistent, since in many cases Rust favors inference and elision to remove boilerplate, while in other cases it requires explicitness when there is no technical reason for it to be required, and other languages handle inference just fine.
I actually the module system is one of the few exceptions where it's actually poorly designed. It could very easily have a 1:1 mapping to the filesystem with no need to declare modules to use them. Lot's of people apparently don't like that idea, but most other module systems do it that way, and IMO it would be a lot more intuitive.
> It could very easily have a 1:1 mapping to the filesystem with no need to declare modules to use them.
I would love to see this. The issue isn't that people hate the idea. The issue is not breaking existing projects and workflows when making such a change. If we could find a way to make this work without breaking existing projects and workflows, I think there's support for doing so.
What if we could provide this with tooling? Like, everyone that uses Go for an extended period eventually complains about how it's an error to import a module and not use it... until they enable goimports-on-save and then the problem vanishes (mostly). (My point is not to compare go/rust module systems but to illuminate how a pain point introduced by the language's guarantees can be eliminated by tooling.)
If there is such an easy to define 1:1 mapping, then could there be a rustmods-on-save tool that automatically adds mod statements according to a fixed scheme whenever any *.rs file is created in the current directory or a child directory?
At the very least, we could have a warning if you have any .rs file that isn't being brought in by a `mod` statement, which would help people understand why their code isn't working.
That'd also get us halfway towards just making it automatically work without `mod` statements.
The new module system is already incompatible with pre-2018 edition projects, so I don't know why this argument explains why the current module system doesn't bite the bullet and use the file system hierarchy.
> The new module system is already incompatible with pre-2018 edition projects
Code written for the 2015 edition should generally just work with the 2018 edition module system. That's part of what we worked to ensure, and that's why we didn't mix in changes that might have reduced that compatibility.
Rust has an RFC process, which is great in many ways. But for something like a module system where everyone has an opinion, it can result in an over-engineered solution that tries to satisfy everyone.
I just wonder how much of that is as-hoc reasoning, and it doesn’t seem like it’s a particularly good sign.