To restate these objections as I understand them:
- Its not great that
use uses different paths than those inside the module.
export into two different keywords is worse than just having
pub use work differently than
- Imports/exports should just use the absolute path syntax.
I think all of these objections are reasonable. I chose pretty opinionated default settings, and I didn’t clearly articulate the motivation for them.
I’ve experimented with applying this and the previous proposal to existing crates (mainly
chalk) and made these observations:
- Absolute paths are rare inside modules; inside modules you almost always want
- Import statements (
use) are mostly from the crate root (
crate::); many are also from extern crates (
extern::); a small number are from this module or the parent (
- Export statement (
pub use), in contrast, are mostly from this module (
Based on this learning, I made these conclusions:
- Import statements should have the crate root as the default, and it should be easy (and hopefully self-explanatory) to import from other crates as well. It should be possible to import from anywhere.
- Export statements should have this module as the default. It should be possible to import from anywhere.
- It should be possible to use a path from anywhere inside a module, but it isn’t super important that it be the most abbreviated form.
And from these conclusions I think the design decisions follow:
The notion of path prefixes
Absolute paths have a prefix, which is one of:
self, refering to this module
super, refering to the parent module
crate, refering to the crate root
extern::<crate>, refering to a dependency
An absolute path, outside of an import/export statement, uses the syntax
There are two motivations here:
- I wanted the default prefix for import to be different than the default prefix for export
- I wanted
use to not introduce items visible outside of this module, even in submodules.
Changing what it means to
use foo::Bar by adding a visibility modifier seems very surprising to me. That
use foo::Bar means to search from the crate root but
pub use foo::Bar means to search from self is troubling.
Of course this is an argument against having different defaults here. Ralf made the case that all paths should have the same default prefix, whereas Josh made the case that import/export statement paths should not have a prefix specified at all. But I think, with two different keywords, having different default settings will be reasonable to learn.
There’s also objection to the
from syntax as the way to change the prefix of a path in import/export statements. I want to point out that its actually shorter in the current form of this proposal for most cases. That is:
from std use io;
In addition to being shorter, I think the first one is more intuitive.
Because absolute paths inside of modules are rare, I think users will learn the
extern::std::io syntax later on, and will at first be using the
from std. I would liken this to type ascription and turbofish. At first, I know that I can do:
let x: Vec<_> = iterator.collect();
But then I have a situation like this, where I don’t want to create a temporary:
s1.parse()? + s2.parse()?
At this point, I learn about the turbofish syntax:
s1.parse::<u32>? + s2.parse::<u32>()?
I would even say that they’re conceptually analogous - creating a temporary is like performing an import, whereas using an absolute path inside your code is like needing to inject the type into the middle of an expression with turbofish.
I might prefer supporting
from as the only syntax for absolute paths also, though I don’t know how I (or anyone else) would feel about that:
let x = from std vec::Vec<i32> = vec!;