Module system changes are going into FCP!

Hey all,

We don't normally post development announcements to the users' forum, but this is one that we'd like your feedback on.

In short, Rust 2018 is going to be simplifying the module system. However, we've been testing out two possible variants of it:

  • Anchored paths, where use statements always start with either an external crate name or a keyword. Importing from submodules requires a leading self::
  • Uniform paths, where use statements can also begin with a local item name, and hence work just like paths elsewhere

A fuller explanation is in the edition guide, and you can try out either on your own code to see how it feels.

@aturon has proposed that we go with anchored paths. However, we'd like people to weigh in before the decision is final.

Full link here: https://github.com/rust-lang/rust/issues/53130#issuecomment-418824862

As always, happy to answer questions here.

17 Likes

To clarify; @aturon has proposed that we, due to deadline concerns, go with the anchored_paths proposals with the caveat that if you use foo; and foo could either be a local module named foo or a crate named foo, then we will produce an error to be forward-compatible with uniform_paths.

7 Likes

I prefer the anchored_paths approach. It seems to me that this approach always makes it unambiguous where an item is coming from. I think this line from the Edition Guide sums it up pretty well:

Much more straightforward.

6 Likes

Bummer discourse doesn't let you do a poll so you can see statistics. Personally, I like the anchored stuff from the 2018 edition preview.

From the few tests i have done anchored looks like a good fit even thought i see a value in the uniform style, too. But with the ability to transition to uniform eventually its a +1 for the proposed way (as i understand it)

I like the anchored path's as well. I just ran into this when I briefly switched to the nightly compiler to run clippy on a project and it just made sense.

I can see where the uniform paths would be useful when prototyping, but as soon as a project starts to grow it can get confusing. Especially when you try to refactor your modules.

1 Like

I definitely prefer anchored paths. Keeping things consistent and easily traceable locally is far superior to me!

2 Likes

I prefer anchored paths as well!

1 Like

I think either way, the 2018 module system will be a big improvement. I'm still leaning towards thinking that uniform paths will be more intuitive in the end, but taking anchored paths now and future-proofing them to allow uniform paths in the future is a great way to learn more before hard choices have to be made.

6 Likes

Firstly, I like anchoring. Some things should be very explicit. The overall map of the source code should be explicit.

I'm more curious about some other module system changes. I like the way submodules with their own submodules are organized, with the submodule name as directory and the submodule itself in that directory in a file called mod.rs.

src/widget/mod.rs <-- mod.rs is the base of the submodule hierarchy.

There are other changes in the edition guide that allow the following to coexist:
src/widget.rs
src/widget/subwidget.rs

Will the new way be mandatory?

No, mod.rs still is allowed to exist. I suspect in the future patterns will develop for which times an inner or outer module file is preferred.

To predict the future, I suspect outer will be preferred for leaf module folders, especially when reexporting platform-specific details, and inner will likely be preferred in deeper module arrangements.

2 Likes

Hmm. So, I'm not 100% clear that I understand the difference between the proposals. Based on the explanation in The Edition Guide it seems to me that the only difference between the proposals is that self:: would be required in use statements in the anchored version, and would not be required in the uniform variant. Is that correct?

Maybe to flesh out what I mean, my intuitive expectation is that paths in Rust follow a similar pattern as filepaths in filesystems. If I'm currently in a directory, I can directly reference names in it. Similarly, I can use relative paths to access content inside sub-directories or parent directories. And finally, I can use "/" or "C:\" (depending on OS) to access root and specify an absolute path.

Because of that, my feeling is that crate::, <crate_name>::, and super:: should be required, but that self:: is mostly superfluous (only needed for disambiguation in certain cases, much like using . at the start of a filepath).

Edit:
Oh, and to further clarify, I do expect paths to behave identically for both use statements and general item referencing. I guess it's more a matter of what that behavior is. I see use as just a way to bring items into local namespace for convenience, and I expect the paths that specify what to bring in to be the same as when referencing items directly.

Edit 2:
More reading suggests that my understanding above is correct. I would like to move to uniform paths eventually, but am also happy with the conservative "achored now, but future-proofing to allow uniform in the future" approach.

To elaborate my preference for uniform paths: any ambiguity in use statements from not using self:: will also arise when referencing items directly, and I feel that handling that problem should also be uniform.

2 Likes

One thing that is not quite clear to me, the summary says:

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

(emphasis added on crate name)

Will this also allow to refer to the create itself by name (instead of saying crate::)? If so that would also allow uniform use of use statements, no? In case referring to the crate itself via its own name is not allowed, then it would be nice if the ambiguity could be resolved in the text.

No, that is not part of any of the proposals. (It doesn't work well because having two crates with the same name is idiomatic, in cases like a library with a small binary driver.)

Luckily this wili does polls:

  • Anchored paths
  • Uniform paths
0 voters
4 Likes

If the goal is to simplify the module system then anchored paths gets my vote. Being explicit is a goal in rust for function level signatures. Lots of other languages would import from root or have some ‘./’ or some other prefix to signify it was a local import. I think explicitly anchored is clearer for people reading the code.

I like explicit. Especially for use statements which aren't written that often. It'll be very nice to be able to read other people's code, and more easily see what types are referred to without having to know the full list of external crates for a project you've just jumped in to. This stuff has generally been confusing enough to me that I've never fully learned the current rules, and instead just try thing until things compile. Generally the first thing I try is to write more explicit full paths.

So anchored paths get my vote.

The biggest annoyance for anchored paths that I see is having to write use self::SomeEnum::*; instead of use SomeEnum::*; when pattern matching. I don't think that's a big deal though.

It will help if the first item in the path is highlighted differently.

e: I am for anchored paths.

2 Likes

Sorry, I don't see it. If the binary driver is the same crate then everything should be fine because you can't reference a binary from other crates, right? Referring to the same crate would exactly be the usecase I had in mind, so no matter whether the binary driver/example (or whatever) is in the same crate, by referring to the crate by name you can simply move the code out to another crate and all will continue to work just fine.

Anyway if that's not allowed, that is fine by me, but I think it should be clearly mentioned in the summary to better differentiate the two different cases, like:

  • Anchored use paths variant: Paths in use declarations always start with a crate name (except the crates' own name), or with crate , super , or self . Paths in code other than use declarations may also start with names relative to the current module.

I like explicit so: Anchored paths :slight_smile: