The Great Module Adventure Continues


FWIW, I also find the direction of moving away from (in my case) leading-:: pretty disappointing.

I don’t like leading-extern because it’s pretty verbose. This is okayish in use statements, I guess, but as far as I’m concerned makes it quite ugly in expression position. Therefore I find the 1path-property somewhat moot in the case of leading-extern: the verbosity makes the ergonomics bad enough to be almost useless.

I don’t like suffix-: because it is too subtle. The distinction between :: and : is easy to gloss over, and this makes it have similar problems to the leading-sigil category of proposals, where we now have another piece of interpunction (which already serves other roles in the language), in the middle of token strings.

Also, before the meeting, it seemed like most participants were leaning towards leading-crate and leading-::, so it’s quite surprising that the meeting suddenly turned people around. Can someone do a more detailed recounting of what’s so bad about fallbacks, what transition options have been discussed and why they have been discounted as realistic options?

Again looking at the table of proposals, I really want leading-::: it has the most consistency of any of the proposals, and the least subtlety. Getting to that global optimum in the longer term should be worth going through some pain in the shorter term. Maybe we just have a feature flag for one epoch?


Just repeating what I said on IRC this morning:

The churn for the module stuff is going to be so painful. And please don’t break extern crate and break a neat way to navigate large code bases and detect renamed crates… I am 100% convinced go-to definition features in editors will never jump to the middle of Cargo.toml if extern crate is dropped. And rls most probably won’t help either given rustc will just be fed command line flags. So I’m curious what tooling would ever restore that.


Would you want it to go to Cargo.toml though? Intellij-Rust downloads the sources for crates and lets you jump into the source, which for me is much more helpful. I can then look at ground-truth for what an external crate’s function is doing.

It seems like that feature wouldn’t be broken by these proposals, but @matklad could probably correct me if I’m wrong.


I want it to jump to where the symbol is defined. In Servo we rename the cookie crate. That’s visible thanks to the extern crate cookie as cookie_rs; statement. If I put the focus on cookie_rs and ask my editor to bring me to the definition and it jumps into the cookie’s crate, I still don’t have any clue what I’m looking at, whereas if it jumps to the extern crate I do, and then RLS or whatever tool can make it such that jumping to the definition of a crate name in an extern crate opens that crate’s code.

That’s similar to what text editor integration already do when jumping to the definition of a renamed symbol (jumping to the definition of foo in a module where there is use bar as foo jumps to the foo in the use statement, I use that very often).


Regardless of the module reform, we can extend the --extern option of rustc with an optional “span” (filename:line:column, or a separate option for Cargo.toml and line:column to --extern to save command-line length, or something else), so cargo can pass it and tools (including error reporting in rustc) can use it.
Even with current rules it would be nice to jump from extern crate items into Cargo.toml, for example.


extern crate could also remain an option until such a fix is definitely working- none of the options make it a hard error or repurpose it even in the next epoch.

Leading-crate/:: even keeps it around in the same sense as today for the transition period. :wink:


Just a quick note: I think it does resolve the ambiguity. After all, ::foo paths are deprecated – so pub Struct(crate :: foo), when you filter to non-deprecated path forms, has only one logical interpretation.

I am still pondering the larger questions.

(Note that the crate visibility modifier is not stable, so we are free to interpret that ambiguity as we want.)


How about new path syntax a.b.c?

use crate.module;

1path, no fallback.


I’m adding my support here for leading extern. I think it’s the clearest to read, and is easy to explain to beginners. It also solves the largest problems IMO which are separating external imports from crate local imports and removing the need to use self:: and super::

I’m also very strongly against any added sigil. I agree with @aturon that it could be a jump-the-shark moment for rust.


Assuming that there’s no syntactic conflict or ambiguity, the dot notation suggested by @liigo is quite elegant, imo. It’s also very much not noisy.

Is the current distinction between :: and . really that important, or can it be an unnecessary obstacle for people trying to learn/read Rust?


If you use an absolute path in your code (rather than in imports on the top), this will lead to unwanted ambiguity


I agree that the uniform dot notation is indeed elegant, however I’d say having a separate :: and . is a good thing - :: refers to a name/module/static scope of things, whereas . suggets that we’re dealing with scope of an instance of something. This is more consistent with String::new, <something as Trait>::some_static etc.

C# has dot notation for imports/modules, static and per-instance fields and frankly the uniformity is confusing. That, coupled with omnipresent PascalCase, means you can’t easily tell what is what.


(random lurker comment) I agree to keep the current :: vs . distinction, even though that syntax looks quite nice. Re: suffix-:, it seems a little odd to make the “big” path boundary (the first one) be smaller than the others, plus it looks kinda vaguely like type bounds. How about suffix-::: or something like that? A little heavier. (If you’re going to introduce a new path separator anyways.)


An evidence that 1path is a useful property: a new user is confused over the difference between use statements and function bodies:


Given the amount of controversy about every proposed “unified module path” syntax… how about we table the whole proposal for a while, and instead improve item resolution errors in the compiler?

So we don’t want to implement item name lookup fallbacks in the compiler for [reasons], but it could still be done for error messages. For example, “Item ‘foo::bar::baz’ not found. Did you mean ‘::foo::bar::baz’?”. (In fact, I think, item name resolution errors in expressions already do that, but some other cases are not covered).
We could also lint “absolute” item name usage in the root module, so it would be not a surprise when it does not work elsewhere.


I do not think joining together the value and type namespaces into one big unholy mess is a very good idea.

struct X;
impl X {
    fn foo(self) { println!("value namespace!") }

mod x {
    pub fn foo() { println!("type/module namespace!") }

fn main() {
    let x = X;;  // currently prints "value namespace!"
    x::foo(); // currently prints "type/module namespace!"


Can’t this be solved by using :: to separate these namespace, and . within each? I.e,


This can be fixed by making all paths start with ::, self or super. No need for extern crate, no need to kill mounting points at arbitrary locations of the module tree, no need for use crate::, etc.


Can’t this be solved by using :: to separate these namespace, and . within each? I.e,

Hmm. I think you could be right…

The two places I can think of where an indentifier at the beginning of an expression is read from the type/value namespace are:

  • a value expression: some number of ::-separated components, ending in a name from the value namespace
  • a struct expression: some number of ::-separated components, all in the type namespace, followed by {

(in either case, a turbofish (::<>) may also follow)

For the second case, I don’t think a.b { } is currently valid syntax. And I can’t think of any counter examples where the first case would cause problems, so long as it had a :: before the last component. (there might be some trickiness with the </> operators and UFCS/generics, maybe?)

Anyways, this isn’t a statement of proof; just one of plausibility.


To be fair though, they seem to have identified how it works on their own, and the confusion is about the motivation for the design. They’re not confused about what the difference is to begin with.