Path clarity

Minimum Rust version: 1.31

The module system is often one of the hardest things for people new to Rust. Everyonehas their own things that take time to master, of course, but there's a rootcause for why it's so confusing to many: while there are simple andconsistent rules defining the module system, their consequences can feelinconsistent, counterintuitive and mysterious.

As such, the 2018 edition of Rust introduces a few new module systemfeatures, but they end up simplifying the module system, to make it moreclear as to what is going on.

Here's a brief summary:

  • extern crate is no longer needed in 99% of circumstances.
  • The crate keyword refers to the current crate.
  • Paths may start with a crate name, even within submodules.
  • Paths starting with :: must reference an external crate.
  • A foo.rs and foo/ subdirectory may coexist; mod.rs is no longer neededwhen placing submodules in a subdirectory.
  • Paths in use declarations work the same as other paths.

These may seem like arbitrary new rules when put this way, but the mentalmodel is now significantly simplified overall. Read on for more details!

More details

Let's talk about each new feature in turn.

No more extern crate

This one is quite straightforward: you no longer need to write extern crate toimport a crate into your project. Before:

  1. // Rust 2015
  2. extern crate futures;
  3. mod submodule {
  4. use futures::Future;
  5. }

After:

  1. // Rust 2018
  2. mod submodule {
  3. use futures::Future;
  4. }

Now, to add a new crate to your project, you can add it to your Cargo.toml,and then there is no step two. If you're not using Cargo, you already had to pass—extern flags to give rustc the location of external crates, so you'd justkeep doing what you were doing there as well.

One small note here: cargo fix will not currently automate this change. We mayhave it do this for you in the future.

An exception

There's one exception to this rule, and that's the "sysroot" crates. These are thecrates distributed with Rust itself. We'd eventually like to remove the requirementfor extern crate for them as well, but it hasn't shipped yet.

You'll need to use extern crate for:

  • proc_macro

Additionally, you would need to use it for:

  • core
  • std

However, extern crate std; is already implicit, and with #![no_std],extern crate core; is already implicit. You'll only need these in highlyspecialized situations.

Finally, on nightly, you'll need it for crates like:

  • alloc
  • test

Macros

One other use for extern crate was to import macros; that's no longer needed.Check the macro section for more.

Renaming crates

If you've been using as to rename your crate like this:

  1. extern crate futures as f;
  2. use f::Future;

then removing the extern crate line on its own won't work. You'll need to do this:

  1. use futures as f;
  2. use self::f::Future;

This change will need to happen in any module that uses f.

The crate keyword refers to the current crate

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

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

Extern crate paths

Previously, using an external crate in a module without a use importrequired a leading :: on the path.

  1. // Rust 2015
  2. extern crate chrono;
  3. fn foo() {
  4. // this works in the crate root
  5. let x = chrono::Utc::now();
  6. }
  7. mod submodule {
  8. fn function() {
  9. // but in a submodule it requires a leading :: if not imported with `use`
  10. let x = ::chrono::Utc::now();
  11. }
  12. }

Now, extern crate names are in scope in the entire crate, includingsubmodules.

  1. // Rust 2018
  2. fn foo() {
  3. // this works in the crate root
  4. let x = chrono::Utc::now();
  5. }
  6. mod submodule {
  7. fn function() {
  8. // crates may be referenced directly, even in submodules
  9. let x = chrono::Utc::now();
  10. }
  11. }

No more mod.rs

In Rust 2015, if you have a submodule:

  1. // This `mod` declaration looks for the `foo` module in
  2. // `foo.rs` or `foo/mod.rs`.
  3. mod foo;

It can live in foo.rs or foo/mod.rs. If it has submodules of its own, itmust be foo/mod.rs. So a bar submodule of foo would live atfoo/bar.rs.

In Rust 2018 the restriction that a module with submodules must be namedmod.rs is lifted. foo.rs can just be foo.rs,and the submodule is still foo/bar.rs. This eliminates the specialname, and if you have a bunch of files open in your editor, you can clearlysee their names, instead of having a bunch of tabs named mod.rs.

Rust 2015Rust 2018
  1. .├── lib.rs└── foo/ ├── mod.rs └── bar.rs
  1. .├── lib.rs├── foo.rs└── foo/ └── bar.rs

use paths

Minimum Rust version: 1.32

Rust 2018 simplifies and unifies path handling compared to Rust 2015. In Rust2015, paths work differently in use declarations than they do elsewhere. Inparticular, paths in use declarations would always start from the crateroot, while paths in other code implicitly started from the current scope.Those differences didn't have any effect in the top-level module, which meantthat everything would seem straightforward until working on a project largeenough to have submodules.

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

Code that looked like this:

  1. // Rust 2015
  2. extern crate futures;
  3. use futures::Future;
  4. mod foo {
  5. pub struct Bar;
  6. }
  7. use foo::Bar;
  8. fn my_poll() -> futures::Poll { ... }
  9. enum SomeEnum {
  10. V1(usize),
  11. V2(String),
  12. }
  13. fn func() {
  14. let five = std::sync::Arc::new(5);
  15. use SomeEnum::*;
  16. match ... {
  17. V1(i) => { ... }
  18. V2(s) => { ... }
  19. }
  20. }

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

  1. // Rust 2018
  2. use futures::Future;
  3. mod foo {
  4. pub struct Bar;
  5. }
  6. use foo::Bar;
  7. fn my_poll() -> futures::Poll { ... }
  8. enum SomeEnum {
  9. V1(usize),
  10. V2(String),
  11. }
  12. fn func() {
  13. let five = std::sync::Arc::new(5);
  14. use SomeEnum::*;
  15. match ... {
  16. V1(i) => { ... }
  17. V2(s) => { ... }
  18. }
  19. }

The same code will also work completely unmodified in a submodule:

  1. // Rust 2018
  2. mod submodule {
  3. use futures::Future;
  4. mod foo {
  5. pub struct Bar;
  6. }
  7. use foo::Bar;
  8. fn my_poll() -> futures::Poll { ... }
  9. enum SomeEnum {
  10. V1(usize),
  11. V2(String),
  12. }
  13. fn func() {
  14. let five = std::sync::Arc::new(5);
  15. use SomeEnum::*;
  16. match ... {
  17. V1(i) => { ... }
  18. V2(s) => { ... }
  19. }
  20. }
  21. }

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

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