Skip to content

Commit

Permalink
Add RFC 385
Browse files Browse the repository at this point in the history
  • Loading branch information
nrc committed Oct 22, 2014
2 parents e38756a + ff8fd73 commit e7ebda6
Showing 1 changed file with 188 additions and 0 deletions.
188 changes: 188 additions & 0 deletions text/0385-module-system-cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# Module system cleanups

- Start Date: 2014-10-10
- RFC PR: [rust-lang/rfcs#385](https://github.com/rust-lang/rfcs/pull/385)
- Rust Issue: [rust-lang/rust#18219](https://github.com/rust-lang/rust/issues/18219)

# Summary

- Lift the hard ordering restriction between `extern crate`, `use` and other items.
- Allow `pub extern crate` as opposed to only private ones.
- Allow `extern crate` in blocks/functions, and not just in modules.

# Motivation

The main motivation is consistency and simplicity:
None of the changes proposed here change the module system in any meaningful way,
they just remove weird forbidden corner cases that are all already possible to express today with workarounds.

Thus, they make it easier to learn the system for beginners, and easier to for developers to evolve their module hierarchies

## Lifting the ordering restriction between `extern crate`, `use` and other items.

Currently, certain items need to be written in a fixed order: First all `extern crate`, then all `use` and then all other items.
This has historically reasons, due to the older, more complex resolution algorithm, which included that shadowing was allowed between those items in that order,
and usability reasons, as it makes it easy to locate imports and library dependencies.

However, after [RFC 50](https://github.com/rust-lang/rfcs/blob/master/complete/0050-no-module-shadowing.md) got accepted there
is only ever one item name in scope from any given source so the historical "hard" reasons loose validity:
Any resolution algorithm that used to first process all `extern crate`, then all `use` and then all items can still do so, it
just has to filter out the relevant items from the whole module body, rather then from sequential sections of it.
And any usability reasons for keeping the order can be better addressed with conventions and lints, rather than hard parser rules.

(The exception here are the special cased prelude, and globs and macros, which are feature gated and out of scope for this proposal)

As it is, today the ordering rule is a unnecessary complication, as it routinely causes beginner to stumble over things like this:

```rust
mod foo;
use foo::bar; // ERROR: Imports have to precede items
```

In addition, it doesn't even prevent certain patterns, as it is possible to work around the order restriction by using a submodule:

```rust
struct Foo;
// One of many ways to expose the crate out of order:
mod bar { extern crate bar; pub use self::bar::x; pub use self::bar::y; ... }
```

Which with this RFC implemented would be identical to

```rust
struct Foo;
extern crate bar;
```

Another use case are item macros/attributes that want to automatically include their their crate dependencies.
This is possible by having the macro expand to an item that links to the needed crate, eg like this:

```rust
#[my_attribute]
struct UserType;
```

Expands to:

```rust
struct UserType;
extern crate "MyCrate" as <gensymb>
impl <gensymb>::MyTrait for UserType { ... }
```

With the order restriction still in place, this requires the sub module workaround, which is unnecessary verbose.

As an example, [gfx-rs](https://github.com/gfx-rs/gfx-rs) currently employs this strategy.

## Allow `pub extern crate` as opposed to only private ones.

`extern crate` semantically is somewhere between `use`ing a module, and declaring one with `mod`,
and is identical to both as far as as the module path to it is considered.
As such, its surprising that its not possible to declare a `extern crate` as public,
even though you can still make it so with an reexport:

```rust

mod foo {
extern crate "bar" as bar_;
pub use bar_ as bar;
}

```

While its generally not neccessary to export a extern library directly, the need for it does arise
occasionally during refactorings of huge crate collections,
generally if a public module gets turned into its own crate.

As an example,the author recalls stumbling over it during a refactoring of gfx-rs.

## Allow `extern crate` in blocks/functions, and not just in modules.

Similar to the point above, its currently possible to both import and declare a module in a
block expression or function body, but not to link to an library:

```rust
fn foo() {
let x = {
extern crate qux; // ERROR: Extern crate not allowed here
use bar::baz; // OK
mod bar { ... }; // OK
qux::foo()
};
}
```

This is again a unnecessary restriction considering that you can declare modules and imports there,
and thus can make an extern library reachable at that point:

```rust
fn foo() {
let x = {
mod qux { extern crate "qux" as qux_; pub use self::qux_ as qux; }
qux::foo()
};
}
```

This again benefits macros and gives the developer the power to place external dependencies
only needed for a single function lexically near it.

## General benefits

In general, the simplification and freedom added by these changes
would positively effect the docs of Rusts module system (which is already often regarded as too complex by outsiders),
and possibly admit other simplifications or RFCs based on the now-equality of view items and items in the module system.

(As an example, the author is considering an RFC about merging the `use` and `type` features;
by lifting the ordering restriction they become more similar and thus more redundant)

This also does not have to be a 1.0 feature, as it is entirely backwards compatible to implement,
and strictly allows more programs to compile than before.
However, as alluded to above it might be a good idea for 1.0 regardless

# Detailed design

- Remove the ordering restriction from resolve
- If necessary, change resolve to look in the whole scope block for view items, not just in a prefix of it.
- Make `pub extern crate` parse and teach privacy about it
- Allow `extern crate` view items in blocks

# Drawbacks

- The source of names in scope might be harder to track down
- Similarly, it might become confusing to see when a library dependency exist.

However, these issues already exist today in one form or another, and can be addressed by proper
docs that make library dependencies clear, and by the fact that definitions are generally greppable in a file.

# Alternatives

As this just cleans up a few aspects of the module system, there isn't really an alternative
apart from not or only partially implementing it.

By not implementing this proposal, the module system remains more complex for the user than necessary.

# Unresolved questions

- Inner attributes occupy the same syntactic space as items and view items, and are currently
also forced into a given order by needing to be written first.
This is also potentially confusing or restrictive for the same reasons as for the view items
mentioned above, especially in regard to the build-in crate attributes, and has one big issue:
It is currently not possible to load a syntax extension
that provides an crate-level attribute, as with the current macro system this would have to be written like this:

```
#[phase(plugin)]
extern crate mycrate;
#![myattr]
```

Which is impossible to write due to the ordering restriction.
However, as attributes and the macro system are also not finalized, this has not been included in
this RFC directly.
- This RFC does also explicitly not talk about wildcard imports and macros in regard to resolution,
as those are feature gated today and likely subject to change. In any case, it seems unlikely that
they will conflict with the changes proposed here, as macros would likely follow
the same module system rules where possible, and wildcard imports would
either be removed, or allowed in a way that doesn't conflict with explicitly imported names to
prevent compilation errors on upstream library changes (new public item may not conflict with downstream items).

0 comments on commit e7ebda6

Please sign in to comment.