pre-RFC: from crate use item


#1
  • Feature Name: from_crate_use_item
  • Start Date: 2017-08-05
  • RFC PR: (leave this empty)
  • Rust Issue: (leave this empty)

Summary

One source of confusion identified in learning Rust is the extern crate statement and its relation to use and mod statements. Part of this confusion is the fact that extern crate introduces the external crate as an item in the root of the current crate, which while being consistent, is hard to reason about when learning and hard to teach. This RFC attempts to introduce a replacement syntax which combines the functionality of extern crate and use statements into a single from <crate> use <item> syntax, with a superset of the current functionality.

Motivation

The primary motivation is to reduce confusion between extern crate and use, while unifying on syntax which is looks like use and is somewhat familiar with other languages.

Guide-level explanation

The following example:

from crate_name use item_name;

Can be viewed as doing the same thing as:

extern crate crate_name;
use crate_name::item_name;

With a single exception, which is that crate_name is not introduced as an item and it is not placed in the current scope. This allows using items from external crates without the names of those crates polluting the current crates namespace unnecessarily. Only used items need to be brought into scope.

Note, it is never possible to have an absolute path in a statement, e.g.

from foo use ::bar;

This is invalid because ::bar refers to the current crate, not an item within foo. This means that this proposal fits well with teaching both current use and a proposed use which prefers relative over absolute paths.

Reference-level explanation

There are a few forms of extern crate which need to be accounted for in order to provide a complete solution, so that extern crate might be deprecated.

extern crate foo;
from foo use self;

extern crate "invalid-identifier" as valid_identifier;
from "invalid-identifier" use self as valid_identifier;

The following sugar could be added to allow for more compact use of the statement:

from foo use {
    x::y,
    a::{b, c},
}

This would be equivalent to:

from foo use x::y;
from foo use a::{b, c};

This new syntax may be used wherever use statements may currently be used.

The design is such that an automated tool could convert every extern crate statement into the appropriate from <crate> use self statement which is equivalent as part of the new epoch proposal.

I believe that from would work as a contextual keyword for this design, given that it only requires two token lookahead to check for the existence of use, which is a keyword.

Note, to reexport a symbol, the syntax is from <crate> pub use <item>;.

Drawbacks

  • The from <crate> use self as alternate_name; syntax is probably more confusing than the current syntax. However, this is unlikely to ever be used by a human and it is the obvious way to create a superset of the current functionality in order to allow automated upgrades of older codebases to the newer syntax.
  • The complexity of this addition to the language may not provide enough benefit to be worth doing.

Rationale and Alternatives

  • This design is focused on unifying extern crate and use through the addition of a targeted feature, which is backwards compatible. Most other designs which accomplish the same thing, are either proposals which include this functionality as a subset or propose a solution which removes functionality in an attempt to simplify.
  • Alternate designs:
  • The impact of not doing this is likely more user confusion about extern crate and use.
  • One alternative would be to introduce a new name lookup syntax which allows specifying a crate, like @crate_name::item_name, such that use @crate_name::item_name; would be the same as from crate_name use item_name;. Whether the @ sigil would be used or a different syntax is currently an open question. I’d prefer to not increase the scope of this proposal to include changes to name lookup outside use-like statements. A concern is that introducing changes to name lookup may make it less familiar to current Rust developers and given how little documentation there is on name lookup currently, it seems best to not increase complexity in this area.

Unresolved questions

  • Should it be from foo pub use bar; or pub use bar from foo;? I currently prefer from foo pub use bar;, mostly because I think the crate name is important, though usually short and the multi-import syntax works better with the crate at the beginning.

[Pre-RFC] Yet another take on modules
#2

What happens with pub?

Realistic example:

from std (pub?) use io::Error;

Looks weird. It might be better for from to be at the end.


#3

Partially repeating my comments from https://github.com/rust-lang/rfcs/pull/2088 :

  • extern crate is quite similar to use, so unifying them may be a good idea.
  • If some path is from another crate it’s immediately visible, which is an extra nicety.
  • Some people like the idea of using other crates without “mounting” them into any place in the current crate (conventionally it’s the root module, which makes it “conventionally special”), this proposal achieves it.
  • from my_crate use path syntax is not optimal because similarly to extern crate it can’t be used “inline” in non-import paths, e.g. let x = my_crate::function(). Some variation of @my_crate::path would be preferable in this sense.
    // Works in all paths, not only imports.
    use @my_crate::path;
    let x = @my_crate::path;
    
    @ itself doesn’t work due to syntactic ambiguities, I’ll try look at the parser and find some short viable replacement.

#4

Maybe [my_crate]::path?


#5

There currently isn’t functionality which allows let x = my_crate::function(); without additional syntax somewhere else. I don’t see why not providing that would be a downside, I’d view it as an additional feature which can be solved outside the scope of this specific RFC. Or is there something I’m missing?

To be clear, I explicitly don’t want to propose syntax that works in all paths, since that can be implemented in an orthogonal way.


#6

I think the point is that having such syntax would render from/use obsolete. You want one or the other, but having both just gives you many solutions to the same problem.


#7

Ok, so that would be a completely different RFC with different goals. Thanks for clarifying.


#8

I would rather say it’s an alternative, more flexible solution to the same general problem (i.e. fodder for your Drawbacks and Alternatives sections). The from syntax as proposed is a bit of a “one trick pony”. Alternatives that cover more use cases are a natural topic to explore.


#9

Yeah, agreed. I’m just busy this weekend and I’m trying to wrap my head around the alternative.


#10

I believe I’ve accounted for the feedback. Let me know if there’s anything else.


#11

This can also be viewed as advantage, because importing a crate is more visible/explicit if it requires an use. It’s sort of a middle ground / compromise between explicitness and locality:

Syntax                     | Visibility/Explicitness | Locality
===================================================================
extern crate my_crate      | +                       | -
-------------------------------------------------------------------
from my_crate use path     | =                       | =
-------------------------------------------------------------------
@my_crate::path            | -                       | +
[my_crate]::path           |                         | 
-------------------------------------------------------------------

#12

extern crate has “locality” as well, it can be used many times in many modules, right near the imports, it’s just not used like that by convention.
I also don’t see why @my_crate / crate::my_crate is less explicit than from crate, in both cases the crate name is highlighted syntactically in some way.

For me the inability to use it inline shifts the proposal into “not sufficiently different from extern crate to go through the change” territory.


#13

But it’s not used like that, presumably because it would be overly verbose and it would import additional symbols into the current modules.

I’d expect that from my_crate use path would shift that convention towards module-local imports.

“Explicit” is probably the wrong word. But it’s more visible at first glance since imports are usually at the beginning of a file (or at least of a module). Inline paths OTOH are buried in the code.

Yes, module-local imports are also possible with extern crate and @my_crate::path, but from my_crate use path encourages it.


#14

It seems like the discussion has moved from being about the RFC as written to about whether an inline syntax should be proposed vs not having an inline syntax at all.

My concern with the inline syntax is purely that it seems like it would be more complicated to explain. I also find that personally, I prefer using language constructs that are more restrictive rather than less restrictive, because when reading code, the meaning of that code is more strict. This probably only slightly applies in this specific case, however.

My primary reason for working on this and the other RFC is because I find the ‘crusade’ to rewrite the module system purely to aide in learning it to be misguided and unlikely to succeed (I think it’s likely to happen, but that’s not success). I want to provide alternatives which provide specific, targeted solutions to specific problems. I also find the fact that all other proposals make file inclusion automatic in some way to be lacking in diversity and focused on the wrong problem.


#15

I want to specifically endorse this statement. I think implicit inclusion of either crates or submodules, especially if it involves globbing, is dangerous and I don’t want to see it in the language.


#16

Same here. I could accept any changes to the module system, as long as no files ever are deduced for compilation from the state of the filesystem. There is not a single issue, I’m more concerned about, as it would be a grave mistake.

I am really happy that idea of qualifying external symbols by some form of special syntax/in the path itself gained traction.