Skip to content
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

Formatting guidelines #2436

Closed
wants to merge 2 commits into from
Closed

Formatting guidelines #2436

wants to merge 2 commits into from

Conversation

nrc
Copy link
Member

@nrc nrc commented May 10, 2018

This RFC defines an official Rust style guide. The style is a specification for the default behaviour of Rustfmt, is required for official Rust projects (including the compiler and standard libraries), and is recommended for all Rust projects. The style guide in this RFC only covers the formatting of code, it does not have any recommendations about how to write idiomatic or high quality Rust code.

Edit: Rendered RFC, and Style Guide Readme

@nrc nrc added the T-dev-tools Relevant to the development tools team, which will review and decide on the RFC. label May 10, 2018
@nrc nrc self-assigned this May 10, 2018
@steveklabnik

This comment has been minimized.

Copy link
Member

@varkor varkor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this looks like a very sensible, well-thought out proposal (unsurprisingly, considering how long it's been maturing). Personally, I think the styles proposed here are almost all readable and aesthetically-pleasing.

My one real reservation is the omission of .-aligning chained method calls / field accesses. (I also prefer trailing |, but then, I know it's impossible to make everyone happy about everything.)

Really nice work!

}
```

If a struct variant is *short* (TODO link to definition), it may be formatted on
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still a TODO here.

}
```

Prefer using a unit struct to an empty struct (these only exist to simplify code
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it'd be helpful if this point was clarified like below (i.e. that "unit struct" refers to struct Foo; and not, e.g., struct Foo();).

```rust
pub trait IndexRanges:
Index<Range<usize>, Output=Self>
Index<RangeTo<usize>, Output=Self>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this format is preferred over trailing , like in comma-separated lists, for consistency?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would have formatted it as follows had I had the choice, it would read like the spine of a list:

pub trait IndexRanges:
      Index<Range<usize>, Output=Self>
      Index<RangeTo<usize>, Output=Self>
      Index<RangeFrom<usize>, Output=Self>
      Index<RangeFull, Output=Self>
{
    ...
}

I do find that a at the end makes it harder to scan structure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're pretty consistent, throughout the document, of putting binary operators (or things that work like binary operators) at the starts of continuation lines.

#### Option - `where_single_line`

`where_single_line` is `false` by default. If `true`, then a where clause with
exactly one component may be formatted on a single line if the rest of the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the rationale for only permitting this for a single component? As long as it fits within a single line, it seems like multiple parameters would still be readable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where clauses get complicated quickly and can be very confusing even if they fit on one line. Furthermore, we reasoned that if a where clause is simple enough to justify being on one line, then it can nearly always be represented as bounds on generic arguments, rather than a where clause.

before:

```rust
pub type Foo: Bar;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indentation.


## Names

* Types shall be `PascalCase`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was decided to use UpperCamelCase rather than PascalCase.


Avoid line-breaking in the signature if possible. If a line break is required in
a non-inherent impl, break immediately before `for`, block indent the concrete type
and put the opening brace on it's own line:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "its".

Do not put any spaces around the `.`.

```rust
x.foo().bar().baz(x, y, z);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the wrapping style:

variable.foo()
        .bar()
        .baz(x, y, z);

should at least be mentioned (if not considered). This is quite prevalent in the Rust codebase and personally, I think it's easier to read than a single level of indentation (where possible). Has there been previous discussion about this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another style is:

variable
    .foo()
    .bar()
    .baz(x, y, z);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Centril: this is mentioned lower down (I probably should have commented further down).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this point is mentioned in the (unlinked) guiding principles document. Smaller diffs should be generally preferred, but I wonder whether this particular point comes at the cost of readability.

Note there is block indent due to the chain and the function call in the above
example.

Prefer formatting the whole chain in mulit-line style and each element on one
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "multi-line".

clause, then you must use the above form:

```rust
a_very_long_pattern
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very much bike-shedding (sorry!), but I much prefer aligning the patterns (including the first), rather than the bars. Is there a good reason not to go for trailing |?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're pretty consistent, throughout the document, of putting binary operators (or things that work like binary operators) at the starts of continuation lines.


```rust
let pattern: Type =
expr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this style hard to read; it obscures structure for me; I'd personally write this as (if it is long...):

let pattern
  : Type
  = expr;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the guideline as currently stated is more consistent. There's a stronger cohesion between pattern and Type from there being no space before the colon.
That motivates having pattern: Type on the same line.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@repax My point was mostly about pattern and = ; moving and aligning the = with the end of let becomes easier to scan imo; but I suppose it might not fit with the overall style.


#### Doc comments

Prefer line comments (`///`) to block comments (`//* ... */`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how "prefer" can fit with "required for official Rust projects". Is it a violation of the rules if someone writes a doc-comment with //*?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is phrasing to straddle the difference between what the Rust project requires, and what we think should be imposed on other comments. Inside the compiler/stdlib, and as far as I know, in all official projects, we require ///. However, while discussing making this a requirement, we decided to relax the wording to "prefer" over "require", as it felt like a bit of an over-reach to mandate this to everyone, and rustfmt basically doesn't touch comments already, so it woudn't even be enforced.

In other words, it's MUST for the Rust project, but SHOULD everywhere else.

```rust
fn foo() {
let x = ...;

Copy link
Contributor

@jethrogb jethrogb May 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the four spaces on this line intentional or not? And if they are intentional, are they normative or not?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks like a bug to me.


If a struct variant is *short* (TODO link to definition), it may be formatted on
one line. In this case, do not use a trailing comma for the field list, but do
put spaces around braces:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: I read "spaces around braces" as Error {err: u32} ,; perhaps inside braces instead?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we specify both around and inside the braces?

Error { err: u32 }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, "spaces around each individual brace." :P

# Guiding principles and rationale

When deciding on style guidelines, the style team tried to be guided by the
following principles (in rough priority order):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great file, but doesn't seemed to be linked from anywhere. Maybe reference it in the readme?

declarations; if imports and modules are separated, then they should be ordered
alphabetically. When sorting, `self` and `super` must come before any other
names. Module declarations should not be moved if they are annotated with
`#[macro_export]`, since that may be semantics changing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macro_export -> macro_use


This RFC defines an official Rust style guide. The style is a specification for the default behaviour of [Rustfmt](https://github.com/rust-lang-nursery/rustfmt), is required for official Rust projects (including the compiler and standard libraries), and is recommended for all Rust projects. The style guide in this RFC only covers the formatting of code, it does not have any recommendations about how to write idiomatic or high quality Rust code.

The formatting guidelines in the style guide have been decided on in the [formatting RFC process](https://github.com/rust-lang/rfcs/blob/master/text/1607-style-rfcs.md) by the style team. The guidelines were [extensively debated](https://github.com/rust-lang-nursery/fmt-rfcs/issues?utf8=✓&q=is:issue) and this RFC is the result of that consensus process. I would like to discourage re-opening debate on the guidelines themselves here. Please limit discussion to the presentation and application of the guide, omissions from the guide, and issues which were missed in the formatting RFC process.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that re-debating is likely unhelpful, but it would be nice if more of the reasonings for the choices made it into the guide, or were easier to find from the relevant sections.

Maybe put more things into "overarching guidelines", and then include reasoning for any places were it's better do things differently? Some things that largely seem true, or were often repeated:

  • keywords should have spaces around them
  • put a space after {/before } (unless they end/start the line)
  • no space before a semicolon
  • single-line lists have separators; multi-line lists have terminators

// This comment goes up to the ................................. 80 char margin.

{
// This comment is .............................................. 80 chars wide.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the rationale, but this suggestion seems very hard to enforce, esp. in combination with "a mechanical formatter should not change comments except to move them within a file".

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the comment means "a mechanical formatter should not change comments except to move them within a line"?

}
```

If a struct variant is *short* (TODO link to definition), it may be formatted on

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/struct/enum

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind this actually. I see what is being said here.

@alexreg
Copy link

alexreg commented May 11, 2018

The link to the actual style guide in the Markdown file is broken.

Prefer to put a comment on its own line. Where a comment follows code, there
should be a single space before it. Where a block comment is inline, there
should be surrounding whitespace as if it were an identifier or keyword. There
should be no trailing whitespace after a comment. Examples:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By trailing whitespace, do you mean whitespace that trails a line comment? Or are you referring to any excessive whitespace immediately after a comment, e. g. 2 chars after a one-line block comment?

To which of the following cases would your rule apply?

Line comment

// This comment has trailing whitespace.       
                                        ^^^^^^^

Between block comment and closing sigil

pub fn foo(/* This is the foo implementation. */ x: T) {...}
pub fn bar(/* Like foo but with bar.          */ y: T) {...}
                                    ^^^^^^^^^

After the closing sigil

pub fn foo(/* This is the foo implementation. */ x: T) {...}
pub fn bar(/* Like foo but with bar. */          y: T) {...}
                                        ^^^^^^^^^

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claui given that the guide also says to prefer // to /*, I don't believe /* and stuff like this was taken into consideration; it's only talking about the line comment style.

that said, in general, we tend not to align things anyway, so it'd be

pub fn foo(/* This is the foo implementation. */ x: T) {...}
pub fn bar(/* Like foo but with bar. */ y: T) {...}

AFAIK.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's only talking about the line comment style.

I see. Would it make sense to discourage the same thing for newlines in multi-line block comments?

“There should be no trailing whitespace after a line comment. Similarly, no line in a multi-line block comment should have trailing whitespace.”

foo => bar,
a_very_long_patten | another_pattern if an_expression() => {
no_room_for_this_expression()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mixing of comma/vs-no comma for match arms always felt kind of inconsistent and annoying to manage as code evolves, so I've been setting match_block_trailing_comma = true. Was there any reasoning around going with match_block_trailing_comma = false as the default?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see - seems to have been brought up way back in the match formatting RFC:

Will we still have the option of configuring this in the future?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably yes, that seems to be a popular option.


Humans comprehend information through pattern matching. By ensuring that all
Rust code has similar formatting, less mental effort is required to comprehend a
new project, lowering the bar to entry for new developers.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nitpick: the phrase is "barrier to entry"


Use spaces, not tabs. Each level of indentation must be four spaces. The maximum
width for a line is 100 characters. A tool should be configurable for all three
of these variables.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might I suggest converting this from a paragraph to a list?

  • Use spaces, not tabs.
  • Each level of indentation must be four spaces.
  • The maximum width for a line is 100 characters.
  • A tool should be configurable for all three of these variables.

Put each attribute on its own line, indented to the indentation of its item.
In the case of inner attributes (`#!`), indent it to the inner indentation (the
indentation of the item 1). Prefer outer attributes, where possible.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are too many "indentations" in this paragraph, imo.

complexity of an item (for example, that all components must be simple names,
not more complex sub-expressions). For more discussion on suitable heuristics,
see the discussion on [this issue](https://github.com/rust-lang-nursery/fmt-rfcs/issues/47).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps remove the second "discussion" in this sentence?

For more discussion on suitable heuristics, see this issue.

the `name` and `version` keys in that order at the top of that section,
followed by the remaining keys other than `description` in alphabetical order,
followed by the `description` at the end of that section.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example would go a long way here, to prevent any ambiguity.


* it is either
- used in expression position (not statement position)
- is an unsafe block in statement position

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure these need to be a sub-list. I think it would read more clearly to remove the "is is either" bullet and pull the following two - lines back and make them * instead.

by `move`), but put a space between the second `|` and the expression of the
closure. Between the `|`s, you should use function definition syntax, however,
elide types where possible.

Copy link

@yoshizzle yoshizzle May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • If at the beginning of a line, do not put a space before the closure's opening |.
  • If the closure is preceded by move, put a single space between move and the opening |.
  • If the closure precedes an expression, put a single space between the closing | and the expression.
  • Between the |s, use function definition syntax.
  • Omit types where possible.

a return type, when there are statements, there are comments in the body, or the
body expression spans multiple lines and is a control-flow expression. If using
braces, follow the rules above for blocks. Examples:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible, omit enclosing {}. However, there are a few circumstances under which {} should be used:

  • When you have a return type.
  • When there are statements.
  • When there are comments in the body.
  • When the body expression spans multiple lines and is a control-flow expression.

If using braces, follow the rules above for blocks. Examples:

```

Functional record update syntax is treated like a field, but it must never have
a trailing comma. There should be no space after `..`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Additionally, there should be no space after ..."


Use a single-line form where possible. There should not be spaces around the
parentheses. Where a single-line form is not possible, each element of the tuple
should be on it's own block-indented line and there should be a trailing comma.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be its and not it's

### Chains of fields and method calls

A chain is a sequence of field accesses and/or method calls. A chain may also
include the try operator. E.g., `a.b.c().d` or `foo?.bar().baz?`.
Copy link

@yoshizzle yoshizzle May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proposed change:

include the try operator ?. For example:


Prefer formatting on one line if possible, and the chain is *small*. If
formatting on multiple lines, each field access or method call in the chain
should be on it's own line with the line-break before the `.` and after any `?`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its not it's

Prefer formatting on one line if possible, and the chain is *small*. If
formatting on multiple lines, each field access or method call in the chain
should be on it's own line with the line-break before the `.` and after any `?`.
Each line should be block-indented. E.g.,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps add a colon after indented and remove E.g.,

@varkor
Copy link
Member

varkor commented May 18, 2018

Would this be the right place to specify formatting guidelines for macro syntax (e.g. list-like macros should accept trailing commas)?

@nrc
Copy link
Member Author

nrc commented Jul 30, 2018

Would this be the right place to specify formatting guidelines for macro syntax (e.g. list-like macros should accept trailing commas)?

Macro syntax is mostly out of scope here. Rustfmt mostly avoids formatting macro definitions and some macro uses and I expect that we'll revisit both the specification and implementation of macro formatting in the future.

nrc added a commit to rust-lang/style-team that referenced this pull request Jul 30, 2018
@nrc
Copy link
Member Author

nrc commented Jul 30, 2018

@rfcbot fcp merge

The RFC is updated with most of the feedback here. Thanks everyone!

@rfcbot
Copy link
Collaborator

rfcbot commented Jul 30, 2018

Team member @nrc has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. labels Jul 30, 2018
@killercup
Copy link
Member

@rfcbot reviewed

@rfcbot rfcbot added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. labels Aug 28, 2018
@japaric
Copy link
Member

japaric commented Aug 28, 2018

@rfcbot reviewed

@rfcbot rfcbot added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. labels Aug 28, 2018
@rfcbot
Copy link
Collaborator

rfcbot commented Oct 9, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot added final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. and removed proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. labels Oct 9, 2018
@fitzgen
Copy link
Member

fitzgen commented Oct 9, 2018

@rfcbot reviewed

This RFC defines an official Rust style guide. The style is a specification for the default behaviour of [Rustfmt](https://github.com/rust-lang-nursery/rustfmt), is required for official Rust projects (including the compiler and standard libraries), and is recommended for all Rust projects. The style guide in this RFC only covers the formatting of code, it does not have any recommendations about how to write idiomatic or high quality Rust code.
@@ -118,7 117,7 @@ fn main() {
### Closures

Don't put any extra spaces before the first `|` (unless the closure is prefixed
by `move`), but put a space between the second `|` and the expression of the
by `move`); put a space between the second `|` and the expression of the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let x = |a| a; // a space before |
test( |a| a ); // a space before |

which one is extra space?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter. The former is covered by always putting spaces around =.

@rfcbot
Copy link
Collaborator

rfcbot commented Oct 19, 2018

The final comment period, with a disposition to merge, as per the review above, is now complete.

@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this RFC. and removed final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. labels Oct 19, 2018
@nrc
Copy link
Member Author

nrc commented Oct 22, 2018

Merged in 3efb02f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this RFC. T-dev-tools Relevant to the development tools team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.