|
| 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