[Pre-RFC] Boil Down Externs


#1

Summary

This RFC proposes to change the current extern crate syntax to allow for multiple crates to be listed.

Motivation

This proposes will improve the ergonomics of the Rust language for projects that have many dependencies.

Detailed design

Implementation would take the current extern crate but allow a list of crates instead of a single crate. A single create would just be a single list item.

Example:

extern crate {
             rocket, rocket_contrib, serde_json as json, chrono,
             dotenv, postgres, r2d2,  r2d2_diesel, tera as template,
             serde_derive, toml, glob
};
pub extern crate {rocket, rocket_contrib};

Meta items before the extern ie. #[macro_use] would be applied to all crates listed.

Example:

#[macro_use]
extern crate {diesel, diesel_codegen, lazy_static, serde_derive};

Duplicate externs would error like it does currently.

How We Teach This

No new names or terminology needed to teach this. Examples should be updated to include the list syntax.

Examples in both books, The Rust Programming Language and Rust by Example, should be updated where more then one crate is used. A small section should be added to http://rustbyexample.com/crates.html

rustfmt should have an opinion how to format the list of crates as well.

Drawbacks

  1. Adds multiple ways to add multiple crates to the language.
  2. Diffs can mask changes in lists.

Alternatives

A crate could be release with a macro. Like the example but one that supports meta values.

macro_rules! externs {
    ( $( $x:ident ),* ) => {
        $(
            extern crate $x;
        )*
    };
}
externs![rocket, rocket_contrib, serde_json]

Unresolved questions

#Updated Added changes based on comments 2017-01-27 last comment


#2

I don’t understand the benefit here. The only motivation is that it improves the ergonomics, but then the 2 drawbacks are both egonomics. I’ve done a lot of Python and they actually suggest in their style guidelines to list imports one-per-line even though their syntax allows multiple for the reasons you list in your drawbacks (plus a few others).

The only downside i can actually think of listing crates singly is that there’s a ton of space at the top that needs to be skipped when browsing files. This is a problem that in Python is handled by the editor (PyCharm does this by default) which auto-collapses the block of import statements at the top. If this is the main ergonomics issue, I’d argue this is an editor problem not a language syntax problem.

In practice I like to keep my imports organized by stdlib, 3rd party, then 1st party and keep things alphabetized within those groups. If I did that using this syntax I know i’d be annoyed in a long list trying to find the location of where I should add the new crate. Doing this would be easier for the 1-import-per-line versus all on one line approach.

Can you show a specific example from an actual project that you think is improved when refactored using this new syntax? I think that would help me understand the improved ergonomics you reference that I can’t see from just thinking about it without a specific example.


#3

I don’t think “it’s not my problem!” is a good response.

If long lists don’t look good, and this is most useful for long lists… maybe that should be addressed?

I think this would be the only case in the language where you have a comma-delimited list of things that isn’t inside some kind of group. (No, wait: where clauses. And you know what? Them not being delimited is a real PITA!) I actually had the exact same idea when I saw that externs! macro posted, but was thinking of using extern crate {...}; instead, like with use.

What about visibility? What about renaming crates?

I don’t think I’d use this, but I also can’t think of any particularly compelling reason to block it, given that I use use x::{y, z}; quite a lot.


#4

Same as @DanielKeep. Since we can do stuff like this:

use std::io:: {
    Read, Write,
    stdin, stdout, stderr,
};

we could allow for things like:

extern crate {
    crate0,
    crate1, crate1_sub,
    crate2, crate2_lib, crate2_help,
};

#5

I don’t think they compare well. The one avoids having to write the module prefix all the time and gives context in a tree-like environment, the other is a flat space.


#6

True, the same thing could be said for the extern {} block though. We could simply have accepted extern fn foo() -> Foo;.

  • Since we have extern {} and use crate::mod::{} it wouldn’t be incoherent to have extern crate {}. The obvious advantage of the block notation is the ability to apply an attribute to the entire block.
  • It makes sense to group per external library/module though, a bit less so to group crates.
  • Still I can imagine some people could want to group related imports. Especially crates with a very long list of dependencies.
  • It does save you from writing extern crate repeatedly
  • However that means they also disappear from diffs.

#7

Good point about auto-collapse. I do not use code folding enough. I also like how you organize your imports.

As for examples, I couldn’t find any popular libraries on crates.io that had a long list. The examples in my rfc are from my recent project for a total of 16 externs + 4 metas. cargo lib currently has 20. Some Servo libs have at least 50.

If a project does not like the list format they can still do a single extern per line.

Thanks! Becker


#8

I don’t think “it’s not my problem!” is a good response.

Good point. I will remove this. Is it really a drawback then?

extern crate {…};

I like this suggestion. Its like use without the namespace prefix. I will update to use this sytnax.

What about visibility? What about renaming crates?

visibility would apply to the whole list. pub extern crate a,b,c

renaming crates would be inline. i will include this in my examples as well extern crate rocket as saturn_v, rocket_contrib, serde_json as json

thank you Becker


#9

I’m really not a fan of adding multiple ways to do things to a language as it makes the language harder to learn because newbies like to ask “What’s the right way?”. It does seem like this is just a tradeoff, do we want to support code-folding of externs in the language or leave that to the editor. I really think it should be left to the editor as keeping the language smaller definitely has its upsides.


#10

I understand and will add this to the list of drawbacks.

Thanks Becker


#11

Yes. “Drawbacks” is a list of reasons this a proposal might not be a good idea. Just because you can’t think of a defense against a particular criticism doesn’t mean it isn’t a drawback.


#12

extern {} blocks have the ability to be annotated with certain attributes that describe context about the block.