I think if there could be a model where a relative pathās first node corresponds to something currently in scope at the current scope-level and all absolute paths are designated with absolute path prefixes in all cases, things would be more clear. If it could be thought of as the prelude and external crate names being brought into top-level nested scopes/modules by the compiler implicitly, then maybe everything would just make sense.
For absolute paths we have (starting with ::):
::<prelude-item>::<rest-of-path>
::crate::<rest-of-path>
::extern::<create-name>::<rest-of-path>
::super::<rest-of-path>
::self::<rest-of-path> (same as a relative path/deprecated)
Relative paths start with a symbol:
<in-scope-module>::<rest-of-path>
<in-scope-enum>::<enum-element>
This is the same for both use and non-use paths.
Now, for what is in-scope, we first have a set of nested, implicit, top-level modules that āuseā the prelude and the external crate names from the cargo.tom/āextern flag:
mod /* unnamed - prelude, created implicitly by the compiler */
{
// compiler adds implicit self alias...
use :: as self
use ::std::prelude::*; // this makes all of the std prelude available at the top-level
use extern::crate; // makes the implicit "crate" module available at the root implicit module
mod extern /* created implicitly by the compiler */
{
// compiler adds implicit self alias...
use ::extern as self
// compiler adds a super alias declaration implicitly....
use :: as super;
// these are added implicitly by the compiler to bring the external crate names (declared in cargo.toml/--extern flag) into scope
use extern crate_1;
use extern crate_2;
...
use extern crate_n;
mod crate /* created implicitly by compiler */
{
// compiler adds implicit self alias...
use ::extern::crate as self
// compiler adds a super alias declaration implicitly....
use ::extern as super;
// use statements and mod declarations at the top-level module in the lib.rs or main.rs
use ...;
use ...;
mod ...
{
// compiler adds implicit self alias...
use ::<absolute-path-to-this-mod> as self
// compiler adds a super alias declaration implicitly to all sub-modules....
use ::<absolute-path-to-parent-module> as super;
...
}
}
}
}
Now, with this formulation of things, the keywords (no longer keywords) in path mapped as follows:
-
::<rest-of-path> is rooted at the implicit top-level prelude including implicit module
-
::<prelude-item>::<rest-of-path> would be selecting unambiguously a particular top-level prelude item and a path relative to that
-
::crate::<rest-of-path> means is rooted in the implicit ācrateā module (which was aliased from ::extern::crate, see top-level)
-
::extern::<rest-of-path> means is rooted at the āexternā implicit module
-
self::<rest-of-path> simply refers to the implicit self alias and is effectively as relative path always (akin to . on fs paths)
-
super::<rest-of-path> simply refers to the implicit super alias and is effectively a relative path always (akin to ⦠on fs paths), so, it would be meaningful to do a path like: super::super::super:::: (akin to āā¦/ā¦/ā¦/ā)
Now, the last bit is that the root element of a relative path would be resolved as:
EDIT: Eliminated searching for symbols in parent/gp/ggp scopes except for extern and prelude scope.
- Symbol in the current module scope? Yes? Use it. No? Continueā¦
Symbol in parent/super scope? Yes? Use it. No? Continueā¦
Symbol in grand-parent scope? Yes? Use it. No? Continueā¦
etc. untilā¦
- Symbol in the implicit extern scope? Yes? Use it. No? Continueā¦
- Symbol in the implicit prelude scope? Yes? Use it. No? Compiler Errorā¦
These rules would make the following relative paths also work correctly:
crate::<rest-of-path>
extern::<extern-crate-name>::<rest-of-path>
self::<rest-of-path>
super::<rest-of-path>
<symbol-in-current-module-scope>::<rest-of-path>
<symbol-in-parent-scope-not-found-in-current-scope>::<rest-of-path>
<symbol-in-grandparent-scope-not-found-sooner>::<rest-of-path>
- etc.
<extern-crate-name-not-otherwise-shadowed-locally>::<rest-of-path
<prelude-symbol-not-otherwise-shadowed-locally>::<rest-of-path>
I think that covers all the cases. To me at least, thinking of it this way, clears up ambiguity. Would warning for shadowing be needed? Perhaps that could then be an optional lint?