-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modules #2121
Modules #2121
Conversation
I like the proposal as a whole, but Otoh, we could make crate visibility the default, and use the existing |
I still have to carefully read everything but I feel that this proposal is underestimating the cost of adding 2 more keywords. |
would be a valid way to invoke rustc by hand: | ||
|
||
``` | ||
rustc src/lib.rs --modules src/*.rs src/**/*.rs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: For clarity, I think this should either be --modules "src/*.rs src/**/*.rs"
or --modules src/*.rs --modules src/**/*.rs
(otherwise src/**/*.rs
would be a "free" argument).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this example, are the arguments expanded by rustc
or the shell? If they are expanded by rustc
, I think the reasoning behind the following statement from the alternatives makes less sense:
We could also put the file-lookup in rustc, instead of cargo, and have rustc perform its own directory walk. We believe this would be a bad choice of layering.
Since rustc
is already doing a directory walk, why not have only rustc do the directory walk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no they are expanded by the shell
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be pointed out in the RFC IMO. I've stumbled across the same question when reading the rendered doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@withoutboats Expansion by shell would never work since there is hard limit on the number arguments you can pass to a program. 128KB worth or 1/4 of your stack size.
I'd have to 👎 the implicit module declaration, specifically on the utility of commenting out The Not to mention that this could break crates which published with broken Rust files that were unreachable with the current rules but would not be with the new rules. |
|
||
* The file name is not a valid Rust identifier followed by `.rs`. | ||
* The file is not in a subdirectory of the directory containing the root | ||
module. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depending on how this "subdirectory of" check is implemented it could get pretty tricky:
- Do the prefixes given to
--modules
have to match? If not: - How are paths normalised? How are soft/hard links handled?
In most cases this probably does not matter, but it may still be worth considering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are implementation details that don't need to be decided at the RFC stage. This is just a sanity check that we can transform the directory into paths.
I like the Items marked as |
This is why the cargo flag is not defaulted to |
I'm wondering about the
Edit: Also, we have |
Constraints for the |
actually `pub`. We make this much clearer by the combination of two choices in | ||
this RFC: | ||
|
||
- All items that are `export` (equivalent of today's `pub`) must *actually* be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I believe this is out of sync with the current version of the RFC.
For file loading, I still think it would be better to move the directory traversal into |
First off, I'm really excited about this RFC! 👍 Now, how about just
✓ Shorter tends to be better than longer. use priv::foo::Bar;
::priv::foo::Bar; ✓ It needs to be sensible as a visibility (an adjective is strongly preferable). |
@repax: The problem that I have with |
@TimNN: Yeah, I think I confused myself there. I dunno. |
@repax: That's exactly what I'm saying, if I understand your analogy correctly:
So saying something is private makes it less private than if I hadn't said anything, which is what I'm having problems with. |
|
||
During the design process, we considered other, more radical redesigns, such as | ||
making all files "inlined" into their directory and/or using the filename to | ||
determine the visibility of a module. We've decided not to steps that are this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
decided not to steps
@TimNN: This is tricky, indeed. But Besides, |
I don't like that I have to change almost all The previously proposed special visibility for items that are part of a library's public API made much more sense to me. For me, something like the following would feel most natural:
This would minimize code churn, since only the (few) exported types/functions of libraries need new visibility qualifiers. |
My comment is very non-verbose to minimize noise. On the preceeding proposal I expressed concerns that privacy level was restricted to three levels (public to the world, public to the crate, private to the module), but that was based on a misunderstanding of mine on the RFC, and I think this RFC doesn't feature this problem either as you can restrict privacy of entire modules via the mod keyword. Stuff I like
Stuff I'm not really a great fan of
My main concerns
Summing it all up👎 |
I appreciate your point, however, for many people the idea of "declaring a module" has proven to be a major source of confusion. In other words, |
While |
As I raised on the previous thread and @TimNN already touched on as well, I think |
|
function, etc), without creating declared items of those names at this point in | ||
the module hierarchy. | ||
|
||
`use` statmeents with a visibility (such as `pub use`) are deprecated, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
statements
I would like to put another voice towards splitting this RFC and moving the autoloading to another RFC. For the record (again, from the other RFC) - I'm against changing the module loading method to be implicit in general, but if it had to be done - using the current proposal of passing a set of globs to rustc is preferable to explicitly listing files (or worse, having rustc know of cargo's rules) |
From how I interpret the RFC, you will have to either make a |
General notesAs a preamble, I would like to note that this RFC acts as if RFC #2088 will land for sure, and AFAIK this has not been decided yet. The same can be said about the reliance on checkpoints in this RFC. I would also like to note that even if a survey said that beginners have issues with the current system, it doesn't automatically mean that the current system should change, and it doesn't mean that the new system (that introduces many very invasive breaking changes) will not make everyone's life more complicated at scale. MotivationThe survey of crates.io didn't include most largest crates, such as the ones in Servo, that uses way more local imports than external ones. In projects that cannot be split into multiple crates, "If a crate is renamed, you do not need to rename all internal imports" shouldn't be an argument in favour of this RFC, that sounds like optimising for crate renaming, which is a very rare thing. The RFC mentions the previous take, where all files were inlined in their directories and directories were modules. It also says that "We've decided not to steps that are this radical right now." Please remove the "right now". Things I don't likeI think that the very existence of implicit module loading will be an additional burden for me to maintain things or contribute to satellite dependencies of Servo that aren't handled by us directly. I think that I have a vague feeling that this RFC will make future versions of the language unable to ever have a module system that is useful for other purposes than simple namespacing (parameterised modules, etc). I think that the mandatory explicit visibility bring absolutely nothing to the table and even hurts ergonomics. Why make Things I think should be blockersThe RFC should not ever, ever land without the automatic migration tool being ready. Things I like🥖, 🥐, and the fact that we can have this discussion. |
I'm gonna write a comment that I've been thinking about since the previous iterations of this RFC: The thing I call the "Rust Module System" in my mind is dead simple:
The fact that a few So, to me, the whole complexity comes from visibility, use and paths, since these topics are more interconnected than how modules are declared. However the core rules around the three topics are also pretty simple, in my opinion: The core rules around visibility are simple:
The rules around paths are simple:
The primary rules around
An then there are some rules, or interaction between rules, which I think cause the additional complexity:
In summary: I think the core rules of the current system are pretty simple (and, more importantly, pretty uniform): They fit on a single sheet of paper. However there are a few rules which are themselves either complex or confusing (the last four rules above). So in my opinion the RFC should focus on improving the situation / learnability around those rules (or accept, eg. for I think this RFC does an admirable job of that with eg. removing extern crates from the root namespace and adding the |
FYI, since rust-lang/rust#42125 landed, there's only one final push required to resolve this for good - make "private-in-public" a lint and not an error and make it use more intuitive reachability-based heuristics (lints can use heuristics, unlike errors). |
I also believe the survey of crates.io is flawed, like @nox says. I'd suggest surveying some large projects like rustc, cargo, servo, ring, and their dependencies. As an aside, there is a cost to users when crates break themselves up into many smaller crates, like the RustCrypto project did, and like the rand crate is discussing, if only because the documentation becomes less cohesive. These crates have security reasons for doing so, in that a person auditing users of the code can see at a glance if approved or unapproved algorithms get included. And tokio makes sense too, due to a very complex dependency graph and that users benefit from understanding. We should not take these hierarchies of micro crates as an ideal though.
I'd love to know how parameterised modules and module aliases fit with this story. I'd assume the module must then contain a line |
Another way to look at it is to go back to @aturon's blog post or the RFC's motivation section which described the issues we're trying to solve. The things auto-loading seems aimed at are "too many declaration forms"/"lots of frontloaded syntax," "filesystem organization," "multiple keywords import things," and "repetition." Some of these goals it still meets, at least individually. However, many of these goals it no longer serves quite as much, or at all. So, while implicit module loading is tied into the local visibility reasoning goal, I'm beginning to think it's not worth it, for a few reasons:
|
One thing I haven't seen discussed is how much an IDE would help the beginner approach the current module system. It seems to me that most of the pains could be avoided at the IDE level. |
Some editorial points: (I’ll leave opinions for a separate comment.) What is seemingly the same visibility keyword is called What is seemingly the same path prefix is called
Grammatically, part of this first sentence seems to be missing. What about these items? Are they disallowed? (Guessing based on context.) “They” in the second sentence seems to refer to these items, but I think something else (the crate’s author?) was intended.
Other parts of the RFC suggest (by using “must”) this is an error rather than a warning. Which is it?
Wouldn’t that need to be |
The attempt to modularize the modules rfc backfired somewhat because the components were overly coupled, so changes in one part caused undetected breakages elsewhere in the system ;) (something something non-local reasoning) |
In this proposal, export is not a visibility modifier. It is statement whose only purpose is to re-export an item from another place. In fact, a visibility modifier can (and usually would be) used with For example,
I believe the correct way to parse the sentence is this: (Items marked pub inside of (files which are not public)) are not visible outside of this crate, because (the full path to that item) is not pub. i.e. the items in question are not public to the world even though they are marked
I think it will be a warning at first; then in a later epoch, it'll be a hard error. |
Or increased focus on The Book's chapters on modules... |
Yesterday, I tried to start cataloging all of the various modules proposals from a birds-eye view in an attempt to identify the differences between them, to record their various advantages/disadvantages, and to help understand the cascading changes in design. I wanted to try and understand the complicated design space that we're dealing with. ...well, that was the plan. After doing this so far, however, it feels to me like the feature of automodules actually is orthogonal to the rest of the design. I notice that, between these two RFCs, this one provides an improved design for |
Also, would it be good to create a separate RFC for the public-in-private lint. It seems like we would want something like that whether this RFC passes or not, and I think it can be done in a forward-compatible way... |
The only problem with the public-in-private lint without this rfc is it forces people to write Presumably the proposal would still have to include a migration tool to do the rewriting in existing code. |
We could just make the lint have a default of
Sure, it's not super pretty and it's not short, but I think it would do nicely until we have a good solution. |
We have done this, twice already. It hasn't helped. Nobody has come up with a magic tutorial that makes people understand things easily. And in fact, @carols10cents , in one of the earlier threads, showed a revised version of the chapter with these changes, and it was a lot better. |
The whole point is to push deployed code towards having the correct visibility modifiers. If it's opt-in, most code won't use it, so it won't have the desired effect. Yeah, people who particularly care can opt in, but that doesn't do anything to address the readability issue when reading other people's code. |
There's one more thing - how compiler's own diagnostics can help the beginner to approach the current module system! Currently there's nearly zero (maybe literally zero) help messages related to module loading (missing EDIT: The first module discussion thread on internals had an immensely useful user report about this - https://internals.rust-lang.org/t/revisiting-rusts-modules/5628/33 |
It is still an intermediate step, though. And whatever the final solution is, I think it will probably include such a lint anyway... |
The modules saga has truly broken all records for comment velocity! After some soul-searching discussions among the lang team, and considering the small amount of time left before the impl period, here's what I'd like to propose:
The new main RFC here involves no new keywords, and can be done purely with deprecations; it does not require a new epoch. I believe it retains many (but not all) of the learnability benefits of the current RFC. It enables us to explore those other benefits in a slower-paced way, through an experimental RFC. @withoutboats is going to be tied up with moving house, so I plan to write this new main RFC in the next couple of days. I expect it to be much shorter and to draw a lot from the Meantime, I'm gonna close this one out... |
@aturon I'm sad that we'll continue to need to write mod statements, but I think that this is the right choice for now. 👍 |
Can path and visibility changes be submitted as separate RFCs?
Oh, this is nice. |
(EDIT: Ah, sorry, it looks like I missed the closing above. I started writing this yesterday, and much of it has been overtaken by events. The underlying reactions, however, might still be useful to someone.) Speaking as commercial user of Rust who's been bringing multiple in-house developers up to speed, and who already has a non-trivial code base to migrate, my feelings about this whole plan are mixed: some positive, some negative, and some wary. This is also the first time I've seen the whole "epochs" RFC, which is apparently a done deal. This is what I get for not reading r/rust all the time.
So overall, my personal verdict is: This change is too large, and it would reduce my trust in Rust's stability and fitness for commercial use. If this is a one-time thing with excellent migration tools, then I can grudgingly accept it to improve Rust onboarding. But if the typical epoch is going to make changes this big, I would need to spend a lot more time defending Rust to my colleagues at work. The mere fact that this will break example code on Stack Overflow is a big deal. Basically, the thought running through the back of my head is "We've had a golden era since Rust 1.0, but is this the end? Will I need to keep up with constant change again?" I simply have too many Rust crates, both at work and personally, to ever chase the upgrade treadmill again. The more I look at the automatic migration tools, parallel epoch support, etc., the more I realize that this isn't an entirely fair reaction. But that's my underlying gut reaction. And that will probably be the gut reaction of some of my colleagues, at least until I explain the whole epochs plan to them, which will take time. Like it or not, those kinds of conversations are also part of the developer experience that's being proposed. |
The new RFC is up! |
Here is another RFC describing a change to the Rust module system (hopefully the last for the near future).
Because it is a relatively large RFC, I have broken the text into multiple files. You can find the rendered root of the RFC here.
The overview document gives a high level overview of the proposed new system and how it differs from the current system.
See RFC #2108, the previous module RFC, for previous work (and links to even more previous work).
For people involved in the previous discussion, here are the highlights of the differences. I would say in summary its a similar skeleton, but with some very significant changes to the skin to make it a much less radical departure.
The
local
keyword: Instead of changing the meaning ofpub
, a new visibility calledlocal
is added for restricted visibility between private and pub. To make this meaning of local easier to learn pedagogically, thecrate::
form of absolute paths have been changed to be writtenlocal::
.mod
statements: Mod statements continue to exist instead of being removed. Even when autoloading modules, they will still be used to control visibility (such as making them more public or more private).The
export
keyword: Now that its not a visibility,export
is just a standard mechanism for re-exporting things, similar to ause
statement but optimized for this use case.Autoloading opt outs: Two mechanisms of opt out for the autoloading mechanism are added, an
#[ignore]
attribute for temporarily opting out of loading a particular file, and a Cargo.toml flag which prevents cargo from preparing the--modules
list at all.Quoting this from the previous RFC:
As a final note: this is a polite reminder that the RFCs repo, like all Rust project venues, is governed by the Code of Conduct and also that we, the RFC authors, are acting in good faith (honestly!). Please always be charitable in disagreement. 😄 🌈 ❤️