Skip to content

Commit e7ebda6

Browse files
committed
Add RFC 385
2 parents e38756a + ff8fd73 commit e7ebda6

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed

text/0385-module-system-cleanup.md

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# Module system cleanups
2+
3+
- Start Date: 2014-10-10
4+
- RFC PR: [rust-lang/rfcs#385](https://github.com/rust-lang/rfcs/pull/385)
5+
- Rust Issue: [rust-lang/rust#18219](https://github.com/rust-lang/rust/issues/18219)
6+
7+
# Summary
8+
9+
- Lift the hard ordering restriction between `extern crate`, `use` and other items.
10+
- Allow `pub extern crate` as opposed to only private ones.
11+
- Allow `extern crate` in blocks/functions, and not just in modules.
12+
13+
# Motivation
14+
15+
The main motivation is consistency and simplicity:
16+
None of the changes proposed here change the module system in any meaningful way,
17+
they just remove weird forbidden corner cases that are all already possible to express today with workarounds.
18+
19+
Thus, they make it easier to learn the system for beginners, and easier to for developers to evolve their module hierarchies
20+
21+
## Lifting the ordering restriction between `extern crate`, `use` and other items.
22+
23+
Currently, certain items need to be written in a fixed order: First all `extern crate`, then all `use` and then all other items.
24+
This has historically reasons, due to the older, more complex resolution algorithm, which included that shadowing was allowed between those items in that order,
25+
and usability reasons, as it makes it easy to locate imports and library dependencies.
26+
27+
However, after [RFC 50](https://github.com/rust-lang/rfcs/blob/master/complete/0050-no-module-shadowing.md) got accepted there
28+
is only ever one item name in scope from any given source so the historical "hard" reasons loose validity:
29+
Any resolution algorithm that used to first process all `extern crate`, then all `use` and then all items can still do so, it
30+
just has to filter out the relevant items from the whole module body, rather then from sequential sections of it.
31+
And any usability reasons for keeping the order can be better addressed with conventions and lints, rather than hard parser rules.
32+
33+
(The exception here are the special cased prelude, and globs and macros, which are feature gated and out of scope for this proposal)
34+
35+
As it is, today the ordering rule is a unnecessary complication, as it routinely causes beginner to stumble over things like this:
36+
37+
```rust
38+
mod foo;
39+
use foo::bar; // ERROR: Imports have to precede items
40+
```
41+
42+
In addition, it doesn't even prevent certain patterns, as it is possible to work around the order restriction by using a submodule:
43+
44+
```rust
45+
struct Foo;
46+
// One of many ways to expose the crate out of order:
47+
mod bar { extern crate bar; pub use self::bar::x; pub use self::bar::y; ... }
48+
```
49+
50+
Which with this RFC implemented would be identical to
51+
52+
```rust
53+
struct Foo;
54+
extern crate bar;
55+
```
56+
57+
Another use case are item macros/attributes that want to automatically include their their crate dependencies.
58+
This is possible by having the macro expand to an item that links to the needed crate, eg like this:
59+
60+
```rust
61+
#[my_attribute]
62+
struct UserType;
63+
```
64+
65+
Expands to:
66+
67+
```rust
68+
struct UserType;
69+
extern crate "MyCrate" as <gensymb>
70+
impl <gensymb>::MyTrait for UserType { ... }
71+
```
72+
73+
With the order restriction still in place, this requires the sub module workaround, which is unnecessary verbose.
74+
75+
As an example, [gfx-rs](https://github.com/gfx-rs/gfx-rs) currently employs this strategy.
76+
77+
## Allow `pub extern crate` as opposed to only private ones.
78+
79+
`extern crate` semantically is somewhere between `use`ing a module, and declaring one with `mod`,
80+
and is identical to both as far as as the module path to it is considered.
81+
As such, its surprising that its not possible to declare a `extern crate` as public,
82+
even though you can still make it so with an reexport:
83+
84+
```rust
85+
86+
mod foo {
87+
extern crate "bar" as bar_;
88+
pub use bar_ as bar;
89+
}
90+
91+
```
92+
93+
While its generally not neccessary to export a extern library directly, the need for it does arise
94+
occasionally during refactorings of huge crate collections,
95+
generally if a public module gets turned into its own crate.
96+
97+
As an example,the author recalls stumbling over it during a refactoring of gfx-rs.
98+
99+
## Allow `extern crate` in blocks/functions, and not just in modules.
100+
101+
Similar to the point above, its currently possible to both import and declare a module in a
102+
block expression or function body, but not to link to an library:
103+
104+
```rust
105+
fn foo() {
106+
let x = {
107+
extern crate qux; // ERROR: Extern crate not allowed here
108+
use bar::baz; // OK
109+
mod bar { ... }; // OK
110+
qux::foo()
111+
};
112+
}
113+
```
114+
115+
This is again a unnecessary restriction considering that you can declare modules and imports there,
116+
and thus can make an extern library reachable at that point:
117+
118+
```rust
119+
fn foo() {
120+
let x = {
121+
mod qux { extern crate "qux" as qux_; pub use self::qux_ as qux; }
122+
qux::foo()
123+
};
124+
}
125+
```
126+
127+
This again benefits macros and gives the developer the power to place external dependencies
128+
only needed for a single function lexically near it.
129+
130+
## General benefits
131+
132+
In general, the simplification and freedom added by these changes
133+
would positively effect the docs of Rusts module system (which is already often regarded as too complex by outsiders),
134+
and possibly admit other simplifications or RFCs based on the now-equality of view items and items in the module system.
135+
136+
(As an example, the author is considering an RFC about merging the `use` and `type` features;
137+
by lifting the ordering restriction they become more similar and thus more redundant)
138+
139+
This also does not have to be a 1.0 feature, as it is entirely backwards compatible to implement,
140+
and strictly allows more programs to compile than before.
141+
However, as alluded to above it might be a good idea for 1.0 regardless
142+
143+
# Detailed design
144+
145+
- Remove the ordering restriction from resolve
146+
- If necessary, change resolve to look in the whole scope block for view items, not just in a prefix of it.
147+
- Make `pub extern crate` parse and teach privacy about it
148+
- Allow `extern crate` view items in blocks
149+
150+
# Drawbacks
151+
152+
- The source of names in scope might be harder to track down
153+
- Similarly, it might become confusing to see when a library dependency exist.
154+
155+
However, these issues already exist today in one form or another, and can be addressed by proper
156+
docs that make library dependencies clear, and by the fact that definitions are generally greppable in a file.
157+
158+
# Alternatives
159+
160+
As this just cleans up a few aspects of the module system, there isn't really an alternative
161+
apart from not or only partially implementing it.
162+
163+
By not implementing this proposal, the module system remains more complex for the user than necessary.
164+
165+
# Unresolved questions
166+
167+
- Inner attributes occupy the same syntactic space as items and view items, and are currently
168+
also forced into a given order by needing to be written first.
169+
This is also potentially confusing or restrictive for the same reasons as for the view items
170+
mentioned above, especially in regard to the build-in crate attributes, and has one big issue:
171+
It is currently not possible to load a syntax extension
172+
that provides an crate-level attribute, as with the current macro system this would have to be written like this:
173+
174+
```
175+
#[phase(plugin)]
176+
extern crate mycrate;
177+
#![myattr]
178+
```
179+
180+
Which is impossible to write due to the ordering restriction.
181+
However, as attributes and the macro system are also not finalized, this has not been included in
182+
this RFC directly.
183+
- This RFC does also explicitly not talk about wildcard imports and macros in regard to resolution,
184+
as those are feature gated today and likely subject to change. In any case, it seems unlikely that
185+
they will conflict with the changes proposed here, as macros would likely follow
186+
the same module system rules where possible, and wildcard imports would
187+
either be removed, or allowed in a way that doesn't conflict with explicitly imported names to
188+
prevent compilation errors on upstream library changes (new public item may not conflict with downstream items).

0 commit comments

Comments
 (0)