Skip to content

Commit

Permalink
Merge pull request #74 from joshtriplett/relative-paths
Browse files Browse the repository at this point in the history
path-clarity: Document new uniform paths variant
  • Loading branch information
Centril authored Aug 13, 2018
2 parents 64123a1 + 42654b6 commit 1da7f77
Showing 1 changed file with 161 additions and 21 deletions.
182 changes: 161 additions & 21 deletions src/rust-2018/path-clarity.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,24 @@ As such, the 2018 edition of Rust introduces a few new module system
features, but they end up *simplifying* the module system, to make it more
clear as to what is going on.

Note: During the 2018 edition preview, there are two variants of the module
system under consideration, the "uniform paths" variant and the "anchored use
paths" variant. Most of these changes apply to both variants; the two variant
sections call out the differences between the two. We encourage testing of the
new "uniform paths" variant introduced in edition preview 2. The release of
Rust 2018 will stabilize one of these two variants and drop the other.

Here's a brief summary:

* `extern crate` is no longer needed
* Absolute paths begin with a crate name, where the keyword `crate`
refers to the current crate.
* The `crate` keyword refers to the current crate.
* Uniform paths variant: Paths work uniformly in both `use` declarations and in
other code. Paths work uniformly both in the top-level module and in
submodules. Any path may start with a crate, with `crate`, `super`, or
`self`, or with a local name relative to the current module.
* Anchored use paths variant: Paths in `use` declarations always start with a
crate name, or with `crate`, `super`, or `self`. Paths in code other than
`use` declarations may also start with names relative to the current module.
* A `foo.rs` and `foo/` subdirectory may coexist; `mod.rs` is no longer needed
when placing submodules in a subdirectory.

Expand Down Expand Up @@ -60,14 +73,142 @@ keep doing what you were doing there as well.
One other use for `extern crate` was to import macros; that's no longer needed.
Check [the macro section](2018/transitioning/modules/macros.html) for more.

### Absolute paths begin with `crate` or the crate name
### The `crate` keyword refers to the current crate.

In `use` declarations and in other code, you can refer to the root of the
current crate with the `crate::` prefix. For instance, `crate::foo::bar` will
always refer to the name `bar` inside the module `foo`, from anywhere else in
the same crate.

The prefix `::` previously referred to either the crate root or an external
crate; it now unambiguously refers to an external crate. For instance,
`::foo::bar` always refers to the name `bar` inside the external crate `foo`.

### Uniform paths variant

The uniform paths variant of Rust 2018 simplifies and unifies path handling
compared to Rust 2015. In Rust 2015, paths work differently in `use`
declarations than they do elsewhere. In particular, paths in `use`
declarations would always start from the crate root, while paths in other code
implicitly started from the current module. Those differences didn't have any
effect in the top-level module, which meant that everything would seem
straightforward until working on a project large enough to have submodules.

In the uniform paths variant of Rust 2018, paths in `use` declarations and in
other code always work the same way, both in the top-level module and in any
submodule. You can always use a relative path from the current module, a path
starting from an external crate name, or a path starting with `crate`, `super`,
or `self`.

Code that looked like this:

```rust,ignore
// Rust 2015
extern crate futures;
use futures::Future;
mod foo {
pub struct Bar;
}
use foo::Bar;
fn my_poll() -> futures::Poll { ... }
enum SomeEnum {
V1(usize),
V2(String),
}
fn func() {
let five = std::sync::Arc::new(5);
use SomeEnum::*;
match ... {
V1(i) => { ... }
V2(s) => { ... }
}
}
```

will look exactly the same in Rust 2018, except that you can delete the `extern
crate` line:

```rust,ignore
// Rust 2018 (uniform paths variant)
use futures::Future;
mod foo {
pub struct Bar;
}
use foo::Bar;
fn my_poll() -> futures::Poll { ... }
enum SomeEnum {
V1(usize),
V2(String),
}
fn func() {
let five = std::sync::Arc::new(5);
use SomeEnum::*;
match ... {
V1(i) => { ... }
V2(s) => { ... }
}
}
```

With Rust 2018, however, the same code will also work completely unmodified in
a submodule:

```rust,ignore
// Rust 2018 (uniform paths variant)
mod submodule {
use futures::Future;
In Rust 2018, paths in `use` statements *must* begin with one of:
mod foo {
pub struct Bar;
}
- A crate name
- `crate` for the current crate's root
- `self` for the current module's root
- `super` for the current module's parent
use foo::Bar;
fn my_poll() -> futures::Poll { ... }
enum SomeEnum {
V1(usize),
V2(String),
}
fn func() {
let five = std::sync::Arc::new(5);
use SomeEnum::*;
match ... {
V1(i) => { ... }
V2(s) => { ... }
}
}
}
```

This makes it easy to move code around in a project, and avoids introducing
additional complexity to multi-module projects.

If a path is ambiguous, such as if you have an external crate and a local
module or item with the same name, you'll get an error, and you'll need to
either rename one of the conflicting names or explicitly disambiguate the path.
To explicitly disambiguate a path, use `::name` for an external crate name, or
`self::name` for a local module or item.

### Anchored use paths variant

In the anchored use paths variant of Rust 2018, paths in `use` declarations
*must* begin with a crate name, `crate`, `self`, or `super`.

Code that looked like this:

Expand All @@ -79,7 +220,7 @@ extern crate futures;
use futures::Future;
mod foo {
struct Bar;
pub struct Bar;
}
use foo::Bar;
Expand All @@ -88,22 +229,22 @@ use foo::Bar;
Now looks like this:

```rust,ignore
// Rust 2018
// Rust 2018 (anchored use paths variant)
// 'futures' is the name of a crate
use futures::Future;
mod foo {
struct Bar;
pub struct Bar;
}
// 'crate' means the current crate
use crate::foo::Bar;
```

In addition, all of these path forms are available outside of `use` statements
as well, which eliminates many sources of confusion. Consider this code in Rust
2015:
In addition, all of these path forms are available outside of `use`
declarations as well, which eliminates many sources of confusion. Consider this
code in Rust 2015:

```rust,ignore
// Rust 2015
Expand Down Expand Up @@ -133,7 +274,7 @@ mod submodule {

In the `futures` example, the `my_poll` function signature is incorrect, because `submodule`
contains no items named `futures`; that is, this path is considered relative. But because
`use` is absolute, `use futures::` works even though a lone `futures::` doesn't! With `std`
`use` is anchored, `use futures::` works even though a lone `futures::` doesn't! With `std`
it can be even more confusing, as you never wrote the `extern crate std;` line at all. So
why does it work in `main` but not in a submodule? Same thing: it's a relative path because
it's not in a `use` declaration. `extern crate std;` is inserted at the crate root, so
Expand All @@ -142,34 +283,33 @@ it's fine in `main`, but it doesn't exist in the submodule at all.
Let's look at how this change affects things:

```rust,ignore
// Rust 2018
// Rust 2018 (anchored use paths variant)
// no more `extern crate futures;`
mod submodule {
// 'futures' is the name of a crate, so this is absolute and works
// 'futures' is the name of a crate, so this is anchored and works
use futures::Future;
// 'futures' is the name of a crate, so this is absolute and works
// 'futures' is the name of a crate, so this is anchored and works
fn my_poll() -> futures::Poll { ... }
}
fn main() {
// 'std' is the name of a crate, so this is absolute and works
// 'std' is the name of a crate, so this is anchored and works
let five = std::sync::Arc::new(5);
}
mod submodule {
fn function() {
// 'std' is the name of a crate, so this is absolute and works
// 'std' is the name of a crate, so this is anchored and works
let five = std::sync::Arc::new(5);
}
}
```

Much more straightforward.

**Note**: an alternative syntax is also under consideration: writing `::some::Local` rather than `crate::some::Local`. If you have thoughts about this alternative, please leave a comment on [the tracking issue](https://github.com/rust-lang/rust/issues/44660) or start a thread on the [edition feedback category](https://internals.rust-lang.org/c/edition-2018-feedback).

### No more `mod.rs`

Expand Down

0 comments on commit 1da7f77

Please sign in to comment.