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

docs(style): add let-chain style rules #110568

Closed

Conversation

calebcartwright
Copy link
Member

No description provided.

@rustbot
Copy link
Collaborator

rustbot commented Apr 20, 2023

r? @yaahc

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-style Relevant to the style team, which will review and decide on the PR/issue. labels Apr 20, 2023
@calebcartwright
Copy link
Member Author

Have opened this as a draft intentionally, both to ensure this accurately reflects the position we came to over several meetings, and to facilitate some discussion around subsequent thoughts and questions I kept waffling on while working on this (added inline below)

Comment on lines +488 to +492
This section covers `for` and `loop` expressions, as well as `if` and `while`
expressions with their sub-expression variants. This includes those with a
single `let` sub-expression (i.e. `if let` and `while let`)
as well as "let-chains": those with one or more `let` sub-expressions and
one or more bool-type conditions (i.e. `if a && let Some(b) = c`).
Copy link
Member Author

Choose a reason for hiding this comment

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

Was ambivalent and self-argumentative around whether it would be more fluid to incorporate the new rules into the existing narratives or to create a net-new section. I eventually landed on the former as I thought it read more naturally and allowed for greater reuse of existing rule prose.

I do not feel strongly, and could be convinced otherwise pretty easily

Comment on lines +518 to +522
If the control line needs to be broken, then prefer breaking before the `=` for any
`let` sub-expression in an `if` or `while` expression that does not fit,
and before `in` in a `for` expression; the following line should be block indented.
If the control line is broken for any reason, then the opening brace should be on its
own line and not indented. Examples:
Copy link
Member Author

Choose a reason for hiding this comment

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

This is one of the aspects that's been a struggle for me, as I feel like there's some important missing text here even without the additions for let-chains.

Specifically, what happens of the pattern itself ends up needing to be formatted across multiple lines?

The rules for let statements cover this scenario, and I imagine (though didn't check) rustfmt would do the same for a non-chained if let. However, the written rules here for expressions do already differ from those for statements (notably whether to break before or after the assignment operator) so I think this is an opportunity for increased clarity and explicitness we should consider for the 2024 edition, if not earlier.

I was tempted to create a new section that covered the let expressions explicitly, but ultimately decided it would be ideal if in the future we could consolidate the rules between the statement and expression contexts, so have punted for now

Comment on lines +551 to +555
if let Some(relatively_long_thing)
= a_long_expression
&& another_long_expression
|| a_third_long_expression
{
Copy link
Member Author

Choose a reason for hiding this comment

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

I think this is a good case for breaking after the assignment operator (rustfmt's current, technically buggy behavior in these control flow expressions, as well as the current behavior and rules for statements). I feel this is technically consistent with current rules, but IMO it would benefit from having the different indentation/spacing for the a_long_expression line instead of the assignment operator in the let expr being the same indent as the binops for the subsequent clauses in the chain

```

A let-chain control line is allowed to be formatted on a single line provided
it only consists of two clauses, separated by `&&`, with the first being an
Copy link
Member Author

Choose a reason for hiding this comment

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

We'd explicitly documented the desire to limit this to &&, so I captured it as such here. I actually don't know whether any other binops are supported within chains, but I think it would be in our best interest to not scope the binops here unless we feel strongly that && vs || should make a difference. I included an example with || below to demonstrate that the concerning scenario I raised against style option 2 (which led us to the eventual option 5 captured in this PR) could still exist if we restrict to &&

Copy link
Contributor

Choose a reason for hiding this comment

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

I actually don't know whether any other binops are supported within chains

|| isn't allowed in a chain if there are any lets. Just FYI, the examples shown here aren't valid due to that rule.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks that's helpful. I think one consideration though is what constitutes valid in the style/formatting context. I.e. at what stage does this get rejected, will it cause the parser to error out prior to creating the AST or does it occur later? Is there a potential for the supported operators to change in the future?

There's existing precedent within the Style Guide that covers "invalid" flavors of syntax, e.g.

https://github.com/rust-lang/rust/blob/master/src/doc/style-guide/src/items.md#where-clauses

// Note that where clauses on `type` aliases are not enforced and should not
// be used.
type Foo<T>
where
    T: Bound
= Bar<T>;

Which I always assume was the case because rustfmt has to account for it so long as the AST can still be produced. If || is rejected during lexing/parsing and we don't think that'll ever change then I'm happy to remove it, but otherwise my inclination would be to update the examples to remove the || while also relaxing the rule to remove the explicit "separated by &&"

Copy link
Contributor

Choose a reason for hiding this comment

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

The validation is not done during parsing. It is in the AST validator (which IIRC, is done just after expansion).

The RFC mentioned that || might be possible in the future, but I'm not aware of any serious consideration for it currently, and I suspect the addition would require another RFC.

Comment on lines +584 to +585
if a
|| let Some(b) = foo()
Copy link
Member Author

Choose a reason for hiding this comment

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

Example referenced above. I do not like the way this looks

@calebcartwright
Copy link
Member Author

cc @rust-lang/style as I guess the team mentions don't go out on draft PRs

@calebcartwright
Copy link
Member Author

Will update this to reflect our plans for where/how we capture nightly-only syntax (#111119)

I'm also going to modify the snippets to remove the ||, though still also planning on relaxing the wording so as to not require rustfmt to implement that bifurcation up front

@bors
Copy link
Contributor

bors commented Jul 21, 2023

☔ The latest upstream changes (presumably #113911) made this pull request unmergeable. Please resolve the merge conflicts.

@compiler-errors
Copy link
Member

We discussed this in the style team triage meeting. It's waiting on draft -- not sure if @calebcartwright is looking for any review here, and afaict the only question was "how do we deal with ||, which imo is not a problem to worry about), so until it's out of draft (or marked back as S-waiting-on-review), i'll mark this as waiting-on-author.

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 27, 2023
@calebcartwright
Copy link
Member Author

Closing because:

  1. There were merge conflicts due to some subsequent structural changes
  2. This is still nightly syntax and we subsequently established an entirely different procedure for nightly syntax https://github.com/rust-lang/style-team/blob/main/nightly-style-procedure.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-style Relevant to the style team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants