-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
imports (use
)
#24
Comments
Here is what I do for my code:
|
I personally also follow @jimmycuadra's "three sections" rule as well, and I find it very beneficial for knowing what's imported at a glance. I imagine it could be hard for rustfmt to distinguish between internal/external crate imports, however :(, so it may not end up making it all the way through. Another rule I personally follow is:
And finally, I personally prefer to never split an import across multiple lines, my preference is to: use foo::{...}; // up to the character limit
use foo::{...}; // remaining imports Again though, just personal preferences and/or ideas! |
Since rustfmt only operates on a file (at least as far as I understand), distinguishing between internal modules and modules in external crates might not be possible, but I could imagine a Cargo integration being able to do it by having Cargo pass information to rustfmt about external crates. |
I also find good having "three sections", possibly 1-line separated. And I like a lot @alexcrichton way to repeat |
The reason I like to use the one-line-per-item format for multi-item imports is that it makes for very clean diffs in addition to making it easy to read. It's simply a line removed for any item removed and a line added for any item added. If you use either of these styles: use foo::{a, b, c, d, e, f, g};
use foo::{h, i, j, k, l, m, n}; use foo::{a, b, c, d, e, f, g,
h, i, j, k, l, m, n}; then changing the items imported often results in a shuffling/reordering of items across lines that makes it less obvious what changed in a diff. |
I follow the same style @alexcrichton described. I do, often, import a module and use a one-level-qualified name at the call site, especially when a module uses highly generic names that assume namespacing. I prefer |
The "three sections" rule seems to be popular and have precedents in other language style guides. I usually use the rule "no more than three segments in a path" and and use imports respectively. |
What I do is: // if they all fit, do this:
use foo::{a, b, c};
// if they fit on one line, but not with the beginning part, do this:
use foo::{
a, b, c
};
// and if they don't fit on one line at all, do this:
use foo::{
a,
b,
c,
}; basically following rustic style guide for function args. I do like the three sections. The one thing I really disagree with from @jimmycuadra is not importing the module itself. I very often import modules in addition to items: use std::fmt::{self, Debug, Display};
impl Display for MyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "hello, world")
}
} |
For a while, I tried to do
etc, but in the end, didn't like it as much as what @jimmycuadra suggested.
The "standard style" here (in my understanding) is to import types directly, but import the parent modules for functions. I really, really like this. I also tend to not use
over
But it seems that I'm in the minority here as well. |
I tend to be in the "explode everything" crowd, and rarely use "{...}". I find it confusing to manipulate and it gets noisy quick. I do glob imports, though, especially for prelude modules, such as |
I believe I generally follow this as well, with one notable exception: I always qualify |
@luser yes, agree 100%. Nice catch. So this is something like "except types which may shadow Prelude types" |
Although I don't do this currently, I like that, too, and wouldn't be disappointed if rustfmt ended up standardizing on that style. Not using glob imports is also not exactly a "style" rule, so I'm not sure that is applicable to rustfmt, although it is part of my personal practice. |
I'm pro what @ubsan said though what @jimmycuadra said about one line per import for clean diffs is is weighing on me and is the first technical argument rather than subjective I've seen so far. Whether stylistically I agree or not ( I do in this case ) the stronger technical argument should win. The diff thing is also just not a case of personally preference, is a better case for making it easier to work in teams on rust code bases. Another thing possibly consider is perhaps instead of two or three conditionally styled cases. Just pick one. I like that about gofmt. Its consistent because the default only has one option for import line breakage. I'm also pro the 3 sections of use clauses. Im not sure if extern lines are out of the scope or not for this discussion but if the aren't, I like all my externs at the top of the file followed by use lines. No technical argument to back that up though. Just my current habit. pub mod foo; is probably in the same arena, maybe for another topic thread... |
@softprops the idea of my style (beside being easy to read) is that a change in the number of arguments is one line diff, while not having so many lines :) |
With changes to the compiler that are currently underway, Rustfmt certainly could get this information without any extra input from Cargo. |
I'd be happy to recommend this, but I don't think Rustfmt should enforce it (it is really easy to implement as a refactoring - rustw and the RLS both support it).
I tend to import traits directly (because you have to) and types directly, but not functions, these I prefer to import the module and use one level of scoping.
Is it worth putting either lower case of upper case - initialled names first? I sometimes do this and think it looks neater, but often don't. If present,
Personally I don't find this useful - it seriously uses up vertical space and I rarely need to scan imports - they are mostly boilerplate. |
Interesting, seems like a good recommendation, I think we might be able to enforce it too, but not until some big name resolution changes land in the compiler
I used to do this, but Rustfmt takes the opposite approach, using visual indentation, and I think I prefer that style, feels more consistent with other formatting. |
Yeah, prelude modules are a good use for glob imports, IMO. |
Agreed. Also, the term I've seen used for "whatever sort does" (implicitly, "in |
I agree that glob imports should mostly be avoided, but not always. One rule I follow more stringently is to never have more than one glob import in scope. With one glob, if I have an unknown name, I know it's either in my list of explicit imports or comes from the one glob, but with multiple globs I have no way of knowing where it comes from without tool support. +1 for |
I do not like this. I always write |
I'm OK with single-line use foo::{bar, baz};
use foo::VeryLongIdentifier;
use foo::ThatWouldMakeLineTooLong; |
We use multi-line The Django coding style guidelines do recommend using They even recommend a tool specifically for formatting Python imports: |
One advantage to never using One advantage to always importing a module (possibly with a short alias), never individual functions or types: people are less likely to manually namespace things if they're already namespaced. For example, in a world where people import types directly, I might declare |
One additional item that came up in today's rust-style meeting: Don't use |
Note that we also need guidelines for using names renamed with One extremely common name conflict I've seen handled multiple ways: use std::fmt; // and use fmt::Write
use std::io; // and use io::Write versus use std::fmt::Write as FmtWrite;
use std::io::Write as IoWrite; |
When reordering imported items, I would like to put them after simple imports. // A
use foo::{
b, d, f, g, h,
a::{x, xx, xxx},
c::{y, yy, yyy},
e::{z, zz, zzz},
};
// B
use foo::{
a::{x, xx, xxx}, b, c::{y, yy, yyy}, d, e::{z, zz, zzz}, f, g, h,
};
// C
use foo::{
a::{x, xx, xxx},
b,
c::{y, yy, yyy},
d,
e::{z, zz, zzz},
f, g, h,
}; |
@topecongiro I very much prefer C in that list, for maintainability. Without keeping the list alphabetized, I find it difficult to quickly scan the list. And on top of that, I often find myself converting one type to the other, when I add or drop an item. For instance, an import of |
Hmm, at first blush, I too prefer alphabetical ordering. However, I think it is worth considering that a nested list is a categorically different kind of import and just like we separate I'm not sure how I feel about the edit distance argument. I feel like we don't have a best practice for when to use nested imports. My instinct is that if you include |
Here’s a potential compromise designed to be human-editable and keep diffs reasonably small without wasting too much vertical space.
If we were importing every module in use std::{
any, ascii,
borrow, boxed,
cell,
char,
clone,
cmp,
collections, convert,
default,
env, error,
f32, f64, ffi, fmt, fs,
hash,
i8, i16, i32, i64, io, isize, iter,
marker, mem,
net, num,
ops, option, os,
panic, path, prelude, process, ptr,
rc, result,
slice, str, string, sync,
thread, time,
u8, u16, u32, u64, usize,
vec,
}; Of course, with a more reasonable maximum line length, there’d be no reason to split up the @@ -1,11 +1,7 @@
use std::{
any, ascii,
borrow, boxed,
- cell,
- char,
- clone,
- cmp,
- collections, convert,
+ cell, char, cmp, collections, convert,
default,
env, error,
f32, f64, ffi, fmt, fs, |
use rocket::{
Request, Response,
request::{self, Form},
response::{Flash, Redirect}
};
use rocket_contrib::JsonValue;
use strum::EnumMessage; Reorder imports in groups and child uses in group of its. |
I'm quite late to this discussion-- I found this thread after requesting that For example: use foo::bar::baz; // one import, one line
use foo::bar::{foo, bar, baz}; // one group, one line
// Don't do this
use foo::bar::{foo::bar, baz};
// Do this
use foo::bar::{
foo::bar,
baz,
};
// Don't do this
use foo::bar::{foo::{bar, baz}, biz};
// Do this
use foo::bar::{
foo::{bar, baz},
biz,
}; |
Going to try and summarise the thread and outstanding issues. To categorise: basic formatting, handling long imports (multi-line formatting), ordering of names in import groups, ordering multiple imports, handling nested imports, recommendations about how to import (e.g., discourage globs, recommend importing modules vs items), normalisation and merging/un-merging imports. Simple formatting
I think this is pretty uncontroversial, the only interesting thing here is no spaces around the braces which we don't do elsewhere, but I think this is well established. RecommendationsI propose we don't make recommendations beyond formatting, simply because I think it is hard work and beyond our remit. Merging/un-merging importsI propose that we not do this by default, but tools may offer options to do this. Large importsWe recommend that import lists should not be split over multiple lines (however, tools won't split them by default). Where an import list does go multi-line, we block indent and allow multiple names per line (but see nested imports below).
Ordering of importsNote that tools which only have access to syntax (such as Rustfmt) cannot tell which imports are from an external crate or the std lib, etc. I propose that newlines are used to group imports. Within a group, we sort imports ascii-betically. Groups of imports are not merged nor re-ordered. E.g., input:
output:
Because of Ordering within an importI propose that names within an import are sorted ascii-betically, but with NormalisationTools must make the following normalisations:
And must apply these recursively. Tools must not otherwise merge or un-merge import lists or adjust glob imports (without an explicit option). Nested importsSee follow-up comment |
Nested importsNote that ordering of nested imports is discussed above. Given the above proposals to reorder, but not to merge nested imports, I think the only remaining question is how to layout. I think there are two possibilities (previously suggested):
I think we could have a heuristic approach too - if the import is small then put on one line, if not split on multiple lines. An example small heuristic here might be no more than one nested import, and the nested import has no more than two component names, or something similar. I think the sentiment on the thread is in favour of always multi-lining nested imports, and I think I agree. |
I'm definitely in the "multi-line nested imports" camp, but at the same time I don't want to see an import with lots of small items split with an item on each line; I really like the nested-import-on-own-line idea. What I'm worried about is this: use foo::{
Bar,
Baz,
Quux,
Corge,
garply::{grault, ...}
}; I would rather have: use foo::{
Bar, Baz, Quux, Corge,
garply::{grault},
}; |
Heavily inspired by discussion here: rust-lang/style-team#24 .
Heavily inspired by discussion here: rust-lang/style-team#24 .
Part of #155 This is fairly standard practice - we group the imports in the `std` > external libraries > current crate categories, separated by newlines. Some links: - rust-lang/style-team#24 - rust-lang/rustfmt#1302 - https://www.reddit.com/r/rust/comments/wwbxhw/comment/ilkid50/
No description provided.
The text was updated successfully, but these errors were encountered: