All right, this thread seems to have died down, so let me try and take a stab at summarizing what people were saying. Let me know obviously if you feel I left something out!
Also available in a gist, where the formatting is slightly better.
The proposals
Let me first re-summarize in a very short-hand form the set of “major proposals” that I’ve seen.
Name | Crate-local use | External use | External in fn body |
---|---|---|---|
Today | use foo::bar; |
use std::cmp::min; |
::std::cmp::min |
leading-crate | use crate::foo::bar; |
use std::cmp::min; |
::std::cmp::min |
leading-::
|
use ::crate::foo::bar; |
use ::std::cmp::min; |
::std::cmp::min |
leading-extern | use crate::foo::bar; |
use extern::std::cmp::min; |
extern::std::cmp::min |
leading-:
|
use :crate::foo::bar; |
use :std::cmp::min; |
:std::cmp::min |
suffix-:
|
use crate:foo::bar; |
use std:cmp::min; |
std:cmp::min |
[crate] |
use [crate]::foo::bar; |
use [std]::cmp::min; |
[std]::cmp::min |
[] |
use []::foo::bar; |
use [std]::cmp::min; |
[std]::cmp::min |
Here was the post proposing each variant, and a few other notes:
- leading-crate is Variant 2 in the original post, and I believe to be what the RFC intended.
- leading-
::
was proposed by @Matklad - leading-extern is Variant 1 in the original post
- leading-
:
was proposed by @withoutboats, along with numerous other syntactic variants - suffix-
:
was described by @aturon here -
[crate]
and[]
were proposed (not initially, but most recently) by @arielb1 here
(There was one proposal I saw but did not include, which is
this one by @dhardy.
I’m not sure that I fully understood it. I think it is saying that
we change was use foo::bar
means, but still permitting ::foo::bar
to be ambiguous, and hence to be leaning quite heavily on fallback –
not just for a transition, but forever?)
How to compare the proposals
Based on the comments made so far, I think there are a number of axes on which we can compare the proposals.
The “1path” property
The first observation is that the proposals can be broken into two
major category. Most of the proposals enjoy the 1path property,
which basically means that the same paths work in function bodies and
use
statements. This means that we can potentially – eventually –
support use foo::bar
with the same meaning as use self::foo::bar
.
In order to make this transition, though, we have to disallow use foo::bar
as legal syntax for an absolute path by the time the next
epoch starts.
Note that we don’t have to actually decide whether to permit relative
paths in use
statements yet. What we do have to decide is whether or
not to clear space to permit us to add it during this epoch. We could
decide to defer this until the next epoch, and thus allow the current
forms and the new forms to co-exist for longer. (Update: See also the
note at the end, “on the cost of transition”.)
Amount of transition
Although it does not have the 1path property, the leading-crate
proposal does offer the smallest transition from today’s
syntax. This because things like use std::cmp::min
still work. All
other things being equal, it seems clear that less transition is
better.
It seems like we can order the amount of transition as follows:
- leading-crate:
- crate-local paths change, but external paths do not
- leading-
::
:- crate-local paths change
- external paths in a
use
change, but to a form that is legal today
- the others:
- crate-local paths change
- external paths change
- the new form is not legal today
It feels to me like the existence of a functional rustfix probably
makes the raw amount of change relatively unimportant. It’s hard
for me to weight the “feeling” of adopting the existing
::std::cmp::min
paths more broadly versus introducing a new form.
@SimonSapin also makes the point that the precise manner of the transition matters a lot, perhaps more than the amount of change. This relates to the next point (coexistence).
Update: See also the note at the end, “on the cost of transition”.
Coexistence
The proposals that introduce a completely new form (everything but
leading-crate and leading-::
) co-exist smoothly with existing paths,
allowing for a simpler, gradual transition. In particular, we can
trivially keep all existing paths working, but simply deprecate those
that we don’t want to support.
(Note that if we want to pave the way to 1path, we do want to make
use
statements require the newer forms in the new epoch. As noted
earlier, we may prefer to leave that for another epoch.)
There are two to adopt leading-crate or leading-::
. I’ll describe in
terms of leading-::
, but the same applies to leading-crate. The
first is the “flag day” approach:
- Leading up to the epoch, we require
extern crate
declarations and we stabilizecrate
-local paths. - We issue a deprecation lint for paths that do not fit into the new model,
suggesting that you adopt either
use ::crate::foo
oruse ::std::foo
. - We also issue a deprecation lint for
extern crate
declarations that use#[macro_import]
or other things, preferring instead to use explicit macro imports. - When you opt into the new epoch, those
extern crate
declarations become dead code, so you can remove them.
This has three downsides:
- you need to make all the changes before moving to the new epoch;
- you also have to make change after moving to the epoch;
- we still require
extern crate
in the leadup to the epoch.
The other approach is the fallback approach. The feasibility of this approach is not yet known; @petrochenkov has indicated it may be plausible.
- We enable both sets of paths early.
- When we see an ambiguous path like
use ::regex::foo
, we first try to resolve in the old style. If that succeeds, we issue a deprecation lint, but allow it to continue.- If that fails, we can use the new semantics.
- Note that name resolution is actually a more complex feedback process, so determining “success” or “failure” can be tricky.
Aesthetic appeal
And of course there is always aesthetic appeal. To some extent, I this is a matter of “getting used to things”, but it’s importance is also not to be underestimated. If people first coming to Rust see what seems to be “heavy” or “ugly” syntax, they may leave before they have a time to get used to it and see it’s inner beauty. =)
Summary of properties
This table summarizes the proposals in terms of the properties above. It’s hard to judge aesthetics, of course, but I’ll note some of the specific concerns that have been raised.
Name | 1path | amount-of-transition | coexist | aesthetics |
---|---|---|---|---|
leading-crate | small | w/ fallback | ||
leading-::
|
yes | medium | w/ fallback | |
leading-extern | yes | high | yes | unbalanced, long in a fn body |
leading-:
|
yes | high | yes | |
suffix-:
|
yes | high | yes |
: vs :: confusing, amb. with type ascription |
[crate] |
yes | high | yes | |
[] |
yes | high | yes |
- The “long in a fn body” comment for leading-extern refers to the
fact that something like
impl extern::fmt::Debug for Bar
feels quite unwieldy. Example comment. - The “unbalanced” comment refers to the way that referencing something from an external path requires an extra segment relative to crate-local paths. Example comment; and another.
- The "
:
vs::
confusing" comment refers to @arielb1’s observation that, if we ever achieve 1path, then the distinction betweenuse foo:bar;
(bar
from cratefoo
) anduse foo::bar;
(foo::bar
relative to local module) would likely lead to confusion.
Observations from the thread
Re-reading the comments in the thread, here are some observations.
- There is strong but not universal support for 1path. It’d be nice to get more clarity on whether we consider this goal crucial.
- Leading extern has some proponents, but is also deemed by many to be too long or unbalanced.
- It would be very useful to validate whether fallover is plausible – @petrochenkov suggests it may work out better than they initially thought. If so, that means that all proposals can coexist.
A few other things
There were a few proposals for tweaks on “nested” syntax that might go along with each variant:
// Suffix-`:` proposal
use std: { ... };
// Leading-`::` proposal
use {
::crate::foo::bar,
::std::fmt,
};
Suggestion
I’d like to encourage everyone to actually write code using these
proposals. I’m tempted to go and implement leading-:
and maybe
suffix-:
as well; I’ve found that writing even a modest amount of
code is very helpful to get a stronger feeling.
Updates
I am updating this post with new proposals and notable bits of new conversation.
On the cost of transition. As discussed in this later comment by @rpjohnst and my response, a move to permit relative paths would “repurpose” existing syntax, which comes at a cost in terms of its effect on existing content in the ecosystem. Even if we want to get there eventually, it is probably wise to move slowly, to give content time to adapt: at the most extreme end, we might begin in this epoch by deprecating the “to be replaced” form, so that you only get warnings. Then, in the next epoch, we might make it a hard error initially, and replace it later on. This a knob we can turn and there was extensive conversation about the tradeoffs in the Epoch RFC itself; it seems like, especially initially, it is probably wise not to “turn too fast”, so as to avoid creating the indelible impression that epochs mean massive churn (which is not the intention).